]>
| Commit | Line | Data |
|---|---|---|
| db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| 65f568bb TG |
2 | |
| 3 | #include <netinet/in.h> | |
| 65f568bb | 4 | |
| 07630cea LP |
5 | #include "sd-netlink.h" |
| 6 | ||
| b5efdb8a | 7 | #include "alloc-util.h" |
| 5cdf13c7 | 8 | #include "ether-addr-util.h" |
| 93a1f792 | 9 | #include "log.h" |
| 8f3c1859 | 10 | #include "memory-util.h" |
| 1c4baffc TG |
11 | #include "netlink-internal.h" |
| 12 | #include "netlink-types.h" | |
| 07630cea | 13 | #include "netlink-util.h" |
| 07630cea | 14 | #include "socket-util.h" |
| 5cdf13c7 | 15 | #include "string-util.h" |
| 8f3c1859 | 16 | #include "strv.h" |
| 65f568bb | 17 | |
| 179b4db4 | 18 | #define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset)) |
| 4ebe732c | 19 | |
| 409856d3 | 20 | int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) { |
| 1c4baffc | 21 | sd_netlink_message *m; |
| 65f568bb | 22 | |
| 1cedca05 YW |
23 | assert(nl); |
| 24 | assert(ret); | |
| 65f568bb | 25 | |
| 409856d3 YW |
26 | /* Note that 'nl' is currently unused, if we start using it internally we must take care to |
| 27 | * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */ | |
| 8c578303 | 28 | |
| 6bf8e24b | 29 | m = new(sd_netlink_message, 1); |
| 65f568bb TG |
30 | if (!m) |
| 31 | return -ENOMEM; | |
| 32 | ||
| 6bf8e24b YW |
33 | *m = (sd_netlink_message) { |
| 34 | .n_ref = 1, | |
| 409856d3 | 35 | .protocol = nl->protocol, |
| 6bf8e24b YW |
36 | .sealed = false, |
| 37 | }; | |
| 65f568bb TG |
38 | |
| 39 | *ret = m; | |
| 65f568bb TG |
40 | return 0; |
| 41 | } | |
| 42 | ||
| 1cedca05 YW |
43 | int message_new_full( |
| 44 | sd_netlink *nl, | |
| 45 | uint16_t nlmsg_type, | |
| 6e196011 | 46 | uint16_t nlmsg_flags, |
| ebf404a4 | 47 | const NLAPolicySet *policy_set, |
| 1cedca05 YW |
48 | size_t header_size, |
| 49 | sd_netlink_message **ret) { | |
| 50 | ||
| 4afd3348 | 51 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
| d8e538ec TG |
52 | size_t size; |
| 53 | int r; | |
| 54 | ||
| 1cedca05 | 55 | assert(nl); |
| ebf404a4 | 56 | assert(policy_set); |
| 1cedca05 | 57 | assert(ret); |
| d8e538ec | 58 | |
| 1cedca05 YW |
59 | size = NLMSG_SPACE(header_size); |
| 60 | assert(size >= sizeof(struct nlmsghdr)); | |
| 12b7dff4 | 61 | |
| 409856d3 | 62 | r = message_new_empty(nl, &m); |
| d8e538ec TG |
63 | if (r < 0) |
| 64 | return r; | |
| 65 | ||
| ebf404a4 | 66 | m->containers[0].policy_set = policy_set; |
| 1b89cf56 | 67 | |
| 1b89cf56 TG |
68 | m->hdr = malloc0(size); |
| 69 | if (!m->hdr) | |
| 70 | return -ENOMEM; | |
| 71 | ||
| 6e196011 | 72 | m->hdr->nlmsg_flags = nlmsg_flags; |
| 1b89cf56 | 73 | m->hdr->nlmsg_len = size; |
| 1cedca05 | 74 | m->hdr->nlmsg_type = nlmsg_type; |
| 1b89cf56 | 75 | |
| 1cc6c93a | 76 | *ret = TAKE_PTR(m); |
| d8e538ec TG |
77 | return 0; |
| 78 | } | |
| 79 | ||
| 6e196011 | 80 | int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, uint16_t nlmsg_flags) { |
| ebf404a4 | 81 | const NLAPolicySet *policy_set; |
| 56fdc16d | 82 | size_t size; |
| 1cedca05 YW |
83 | int r; |
| 84 | ||
| 85 | assert_return(nl, -EINVAL); | |
| 86 | assert_return(ret, -EINVAL); | |
| 87 | ||
| 6e196011 | 88 | r = netlink_get_policy_set_and_header_size(nl, nlmsg_type, nlmsg_flags, &policy_set, &size); |
| 1cedca05 YW |
89 | if (r < 0) |
| 90 | return r; | |
| 91 | ||
| 6e196011 | 92 | return message_new_full(nl, nlmsg_type, nlmsg_flags, policy_set, size, ret); |
| 1cedca05 YW |
93 | } |
| 94 | ||
| 409856d3 YW |
95 | int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) { |
| 96 | struct nlmsgerr *err; | |
| 97 | int r; | |
| 98 | ||
| 99 | assert(error <= 0); | |
| 100 | ||
| 6e196011 | 101 | r = message_new(nl, ret, NLMSG_ERROR, 0); |
| 409856d3 YW |
102 | if (r < 0) |
| 103 | return r; | |
| 104 | ||
| 105 | message_seal(*ret); | |
| 106 | (*ret)->hdr->nlmsg_seq = serial; | |
| 107 | ||
| 108 | err = NLMSG_DATA((*ret)->hdr); | |
| 109 | err->error = error; | |
| 110 | ||
| 111 | return 0; | |
| 112 | } | |
| 113 | ||
| dd35a61c | 114 | int sd_netlink_message_set_request_dump(sd_netlink_message *m, int dump) { |
| 6e20c8f8 TG |
115 | assert_return(m, -EINVAL); |
| 116 | assert_return(m->hdr, -EINVAL); | |
| ec44d3f4 YW |
117 | assert_return(m->protocol != NETLINK_ROUTE || |
| 118 | IN_SET(m->hdr->nlmsg_type, | |
| ffeb16f5 | 119 | RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, |
| e8b8b3ea YW |
120 | RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP, RTM_GETQDISC, RTM_GETTCLASS), |
| 121 | -EINVAL); | |
| 6e20c8f8 | 122 | |
| 5883ff60 | 123 | SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); |
| 6e20c8f8 TG |
124 | |
| 125 | return 0; | |
| 126 | } | |
| 127 | ||
| f23ab4dc | 128 | DEFINE_TRIVIAL_REF_FUNC(sd_netlink_message, sd_netlink_message); |
| 65f568bb | 129 | |
| dd35a61c | 130 | sd_netlink_message* sd_netlink_message_unref(sd_netlink_message *m) { |
| f23ab4dc | 131 | while (m && --m->n_ref == 0) { |
| 3dd215e0 TG |
132 | unsigned i; |
| 133 | ||
| 65f568bb | 134 | free(m->hdr); |
| 3dd215e0 | 135 | |
| 9f5bbfe3 | 136 | for (i = 0; i <= m->n_containers; i++) |
| f663aeb8 | 137 | free(m->containers[i].attributes); |
| 3dd215e0 | 138 | |
| f23ab4dc | 139 | sd_netlink_message *t = m; |
| 82e4eda6 DH |
140 | m = m->next; |
| 141 | free(t); | |
| 65f568bb TG |
142 | } |
| 143 | ||
| 144 | return NULL; | |
| 145 | } | |
| 146 | ||
| dd35a61c | 147 | int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *ret) { |
| 65f568bb | 148 | assert_return(m, -EINVAL); |
| ebf404a4 | 149 | assert_return(ret, -EINVAL); |
| 65f568bb | 150 | |
| ebf404a4 | 151 | *ret = m->hdr->nlmsg_type; |
| 65f568bb TG |
152 | |
| 153 | return 0; | |
| 154 | } | |
| 155 | ||
| dd35a61c | 156 | int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) { |
| c06cb593 | 157 | assert_return(m, -EINVAL); |
| a22a8698 | 158 | assert_return(flags != 0, -EINVAL); |
| c06cb593 SS |
159 | |
| 160 | m->hdr->nlmsg_flags = flags; | |
| 161 | ||
| 162 | return 0; | |
| 163 | } | |
| 164 | ||
| dd35a61c | 165 | int sd_netlink_message_is_broadcast(sd_netlink_message *m) { |
| 1f0db3ed TG |
166 | assert_return(m, -EINVAL); |
| 167 | ||
| 4d4d898a | 168 | return m->multicast_group != 0; |
| 1f0db3ed TG |
169 | } |
| 170 | ||
| ebf404a4 YW |
171 | /* If successful the updated message will be correctly aligned, if unsuccessful the old message is untouched. */ |
| 172 | static int add_rtattr(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t data_length) { | |
| 6497a8aa | 173 | size_t message_length; |
| 65f568bb TG |
174 | struct nlmsghdr *new_hdr; |
| 175 | struct rtattr *rta; | |
| 7ca1d319 | 176 | int offset; |
| 65f568bb | 177 | |
| 33125ac5 TG |
178 | assert(m); |
| 179 | assert(m->hdr); | |
| e5c4350b | 180 | assert(!m->sealed); |
| 33125ac5 | 181 | assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); |
| 6497a8aa | 182 | assert(!data || data_length > 0); |
| 4ebe732c ZJS |
183 | |
| 184 | /* get the new message size (with padding at the end) */ | |
| 6497a8aa | 185 | message_length = m->hdr->nlmsg_len + RTA_SPACE(data_length); |
| 65f568bb | 186 | |
| 05d0c2e3 JT |
187 | /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ |
| 188 | if (message_length > MIN(page_size(), 8192UL)) | |
| 189 | return -ENOBUFS; | |
| 190 | ||
| 65f568bb TG |
191 | /* realloc to fit the new attribute */ |
| 192 | new_hdr = realloc(m->hdr, message_length); | |
| 193 | if (!new_hdr) | |
| 194 | return -ENOMEM; | |
| 195 | m->hdr = new_hdr; | |
| 196 | ||
| 197 | /* get pointer to the attribute we are about to add */ | |
| 6497a8aa YW |
198 | rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len); |
| 199 | ||
| ebf404a4 | 200 | rtattr_append_attribute_internal(rta, attr_type, data, data_length); |
| 65f568bb | 201 | |
| 5a081409 | 202 | /* if we are inside containers, extend them */ |
| c7209bcf | 203 | for (unsigned i = 0; i < m->n_containers; i++) |
| 6497a8aa | 204 | GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length); |
| 7ca1d319 | 205 | |
| 4ebe732c | 206 | /* update message size */ |
| 6497a8aa | 207 | offset = m->hdr->nlmsg_len; |
| 4ebe732c ZJS |
208 | m->hdr->nlmsg_len = message_length; |
| 209 | ||
| 6497a8aa | 210 | /* return old message size */ |
| 7ca1d319 | 211 | return offset; |
| 65f568bb TG |
212 | } |
| 213 | ||
| ebf404a4 YW |
214 | static int message_attribute_has_type(sd_netlink_message *m, size_t *ret_size, uint16_t attr_type, NLAType type) { |
| 215 | const NLAPolicy *policy; | |
| d8e538ec | 216 | |
| 6c14ad61 DH |
217 | assert(m); |
| 218 | ||
| ebf404a4 YW |
219 | policy = policy_set_get_policy(m->containers[m->n_containers].policy_set, attr_type); |
| 220 | if (!policy) | |
| 98be4292 | 221 | return -EOPNOTSUPP; |
| d8e538ec | 222 | |
| ebf404a4 | 223 | if (policy_get_type(policy) != type) |
| d8e538ec TG |
224 | return -EINVAL; |
| 225 | ||
| ebf404a4 YW |
226 | if (ret_size) |
| 227 | *ret_size = policy_get_size(policy); | |
| 6c14ad61 | 228 | return 0; |
| d8e538ec TG |
229 | } |
| 230 | ||
| dd35a61c | 231 | int sd_netlink_message_append_string(sd_netlink_message *m, uint16_t attr_type, const char *data) { |
| d8e538ec | 232 | size_t length, size; |
| 0a0dc69b | 233 | int r; |
| 65f568bb TG |
234 | |
| 235 | assert_return(m, -EINVAL); | |
| e5c4350b | 236 | assert_return(!m->sealed, -EPERM); |
| 65f568bb TG |
237 | assert_return(data, -EINVAL); |
| 238 | ||
| ebf404a4 | 239 | r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING); |
| 0a0dc69b TG |
240 | if (r < 0) |
| 241 | return r; | |
| 65f568bb | 242 | |
| d8e538ec | 243 | if (size) { |
| 3072eecf LP |
244 | length = strnlen(data, size+1); |
| 245 | if (length > size) | |
| d8e538ec TG |
246 | return -EINVAL; |
| 247 | } else | |
| 248 | length = strlen(data); | |
| 33125ac5 | 249 | |
| ebf404a4 | 250 | r = add_rtattr(m, attr_type, data, length + 1); |
| 0a0dc69b TG |
251 | if (r < 0) |
| 252 | return r; | |
| 253 | ||
| 254 | return 0; | |
| 255 | } | |
| 256 | ||
| dd35a61c | 257 | int sd_netlink_message_append_strv(sd_netlink_message *m, uint16_t attr_type, const char* const *data) { |
| 6d725977 | 258 | size_t length, size; |
| 6d725977 YW |
259 | int r; |
| 260 | ||
| 261 | assert_return(m, -EINVAL); | |
| 262 | assert_return(!m->sealed, -EPERM); | |
| 263 | assert_return(data, -EINVAL); | |
| 264 | ||
| ebf404a4 | 265 | r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING); |
| 6d725977 YW |
266 | if (r < 0) |
| 267 | return r; | |
| 268 | ||
| 269 | STRV_FOREACH(p, data) { | |
| 270 | if (size) { | |
| 271 | length = strnlen(*p, size+1); | |
| 272 | if (length > size) | |
| 273 | return -EINVAL; | |
| 274 | } else | |
| 275 | length = strlen(*p); | |
| 276 | ||
| ebf404a4 | 277 | r = add_rtattr(m, attr_type, *p, length + 1); |
| 6d725977 YW |
278 | if (r < 0) |
| 279 | return r; | |
| 280 | } | |
| 281 | ||
| 282 | return 0; | |
| 283 | } | |
| 284 | ||
| dd35a61c | 285 | int sd_netlink_message_append_flag(sd_netlink_message *m, uint16_t attr_type) { |
| c06aead0 SS |
286 | size_t size; |
| 287 | int r; | |
| 288 | ||
| 289 | assert_return(m, -EINVAL); | |
| 290 | assert_return(!m->sealed, -EPERM); | |
| 291 | ||
| ebf404a4 | 292 | r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_FLAG); |
| c06aead0 SS |
293 | if (r < 0) |
| 294 | return r; | |
| 295 | ||
| ebf404a4 | 296 | r = add_rtattr(m, attr_type, NULL, 0); |
| c06aead0 SS |
297 | if (r < 0) |
| 298 | return r; | |
| 299 | ||
| 300 | return 0; | |
| 301 | } | |
| 302 | ||
| dd35a61c | 303 | int sd_netlink_message_append_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t data) { |
| 7b179640 SS |
304 | int r; |
| 305 | ||
| 306 | assert_return(m, -EINVAL); | |
| 307 | assert_return(!m->sealed, -EPERM); | |
| 308 | ||
| ebf404a4 | 309 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U8); |
| 7b179640 SS |
310 | if (r < 0) |
| 311 | return r; | |
| 312 | ||
| ebf404a4 | 313 | r = add_rtattr(m, attr_type, &data, sizeof(uint8_t)); |
| 7b179640 SS |
314 | if (r < 0) |
| 315 | return r; | |
| 316 | ||
| 317 | return 0; | |
| 318 | } | |
| 319 | ||
| dd35a61c | 320 | int sd_netlink_message_append_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t data) { |
| 01b36069 TG |
321 | int r; |
| 322 | ||
| 323 | assert_return(m, -EINVAL); | |
| e5c4350b | 324 | assert_return(!m->sealed, -EPERM); |
| 01b36069 | 325 | |
| ebf404a4 | 326 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U16); |
| 01b36069 TG |
327 | if (r < 0) |
| 328 | return r; | |
| 329 | ||
| ebf404a4 | 330 | r = add_rtattr(m, attr_type, &data, sizeof(uint16_t)); |
| 01b36069 TG |
331 | if (r < 0) |
| 332 | return r; | |
| 333 | ||
| 334 | return 0; | |
| 335 | } | |
| 336 | ||
| dd35a61c | 337 | int sd_netlink_message_append_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t data) { |
| 0a0dc69b TG |
338 | int r; |
| 339 | ||
| 340 | assert_return(m, -EINVAL); | |
| e5c4350b | 341 | assert_return(!m->sealed, -EPERM); |
| 0a0dc69b | 342 | |
| ebf404a4 | 343 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U32); |
| 0a0dc69b TG |
344 | if (r < 0) |
| 345 | return r; | |
| 346 | ||
| ebf404a4 | 347 | r = add_rtattr(m, attr_type, &data, sizeof(uint32_t)); |
| 0a0dc69b TG |
348 | if (r < 0) |
| 349 | return r; | |
| 350 | ||
| 351 | return 0; | |
| 352 | } | |
| 353 | ||
| dd35a61c | 354 | int sd_netlink_message_append_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t data) { |
| 81962db7 SS |
355 | int r; |
| 356 | ||
| 357 | assert_return(m, -EINVAL); | |
| 358 | assert_return(!m->sealed, -EPERM); | |
| 359 | ||
| ebf404a4 | 360 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U64); |
| 81962db7 SS |
361 | if (r < 0) |
| 362 | return r; | |
| 363 | ||
| ebf404a4 | 364 | r = add_rtattr(m, attr_type, &data, sizeof(uint64_t)); |
| 81962db7 SS |
365 | if (r < 0) |
| 366 | return r; | |
| 367 | ||
| 368 | return 0; | |
| 369 | } | |
| 370 | ||
| dd35a61c | 371 | int sd_netlink_message_append_s8(sd_netlink_message *m, uint16_t attr_type, int8_t data) { |
| aa550d2a SS |
372 | int r; |
| 373 | ||
| 374 | assert_return(m, -EINVAL); | |
| 375 | assert_return(!m->sealed, -EPERM); | |
| 376 | ||
| ebf404a4 | 377 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S8); |
| aa550d2a SS |
378 | if (r < 0) |
| 379 | return r; | |
| 380 | ||
| ebf404a4 | 381 | r = add_rtattr(m, attr_type, &data, sizeof(int8_t)); |
| aa550d2a SS |
382 | if (r < 0) |
| 383 | return r; | |
| 384 | ||
| 385 | return 0; | |
| 386 | } | |
| 387 | ||
| dd35a61c | 388 | int sd_netlink_message_append_s16(sd_netlink_message *m, uint16_t attr_type, int16_t data) { |
| aa550d2a SS |
389 | int r; |
| 390 | ||
| 391 | assert_return(m, -EINVAL); | |
| 392 | assert_return(!m->sealed, -EPERM); | |
| 393 | ||
| ebf404a4 | 394 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S16); |
| aa550d2a SS |
395 | if (r < 0) |
| 396 | return r; | |
| 397 | ||
| ebf404a4 | 398 | r = add_rtattr(m, attr_type, &data, sizeof(int16_t)); |
| aa550d2a SS |
399 | if (r < 0) |
| 400 | return r; | |
| 401 | ||
| 402 | return 0; | |
| 403 | } | |
| 404 | ||
| dd35a61c | 405 | int sd_netlink_message_append_s32(sd_netlink_message *m, uint16_t attr_type, int32_t data) { |
| aa550d2a SS |
406 | int r; |
| 407 | ||
| 408 | assert_return(m, -EINVAL); | |
| 409 | assert_return(!m->sealed, -EPERM); | |
| 410 | ||
| ebf404a4 | 411 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S32); |
| aa550d2a SS |
412 | if (r < 0) |
| 413 | return r; | |
| 414 | ||
| ebf404a4 | 415 | r = add_rtattr(m, attr_type, &data, sizeof(int32_t)); |
| aa550d2a SS |
416 | if (r < 0) |
| 417 | return r; | |
| 418 | ||
| 419 | return 0; | |
| 420 | } | |
| 421 | ||
| dd35a61c | 422 | int sd_netlink_message_append_s64(sd_netlink_message *m, uint16_t attr_type, int64_t data) { |
| aa550d2a SS |
423 | int r; |
| 424 | ||
| 425 | assert_return(m, -EINVAL); | |
| 426 | assert_return(!m->sealed, -EPERM); | |
| 427 | ||
| ebf404a4 | 428 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S64); |
| aa550d2a SS |
429 | if (r < 0) |
| 430 | return r; | |
| 431 | ||
| ebf404a4 | 432 | r = add_rtattr(m, attr_type, &data, sizeof(int64_t)); |
| aa550d2a SS |
433 | if (r < 0) |
| 434 | return r; | |
| 435 | ||
| 436 | return 0; | |
| 437 | } | |
| 438 | ||
| dd35a61c | 439 | int sd_netlink_message_append_data(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t len) { |
| 17af840b SS |
440 | int r; |
| 441 | ||
| 442 | assert_return(m, -EINVAL); | |
| 443 | assert_return(!m->sealed, -EPERM); | |
| 444 | ||
| ebf404a4 | 445 | r = add_rtattr(m, attr_type, data, len); |
| 17af840b SS |
446 | if (r < 0) |
| 447 | return r; | |
| 448 | ||
| 449 | return 0; | |
| 450 | } | |
| 451 | ||
| dd35a61c | 452 | int sd_netlink_message_append_container_data( |
| 2c08455e | 453 | sd_netlink_message *m, |
| ebf404a4 YW |
454 | uint16_t container_type, |
| 455 | uint16_t attr_type, | |
| 2c08455e YW |
456 | const void *data, |
| 457 | size_t len) { | |
| 458 | ||
| 459 | int r; | |
| 460 | ||
| 461 | assert_return(m, -EINVAL); | |
| 462 | assert_return(!m->sealed, -EPERM); | |
| 463 | ||
| 464 | r = sd_netlink_message_open_container(m, container_type); | |
| 465 | if (r < 0) | |
| 466 | return r; | |
| 467 | ||
| ebf404a4 | 468 | r = sd_netlink_message_append_data(m, attr_type, data, len); |
| 2c08455e YW |
469 | if (r < 0) |
| 470 | return r; | |
| 471 | ||
| 472 | return sd_netlink_message_close_container(m); | |
| 473 | } | |
| 474 | ||
| ebf404a4 | 475 | int netlink_message_append_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, const union in_addr_union *data) { |
| 0a0dc69b TG |
476 | int r; |
| 477 | ||
| 478 | assert_return(m, -EINVAL); | |
| e5c4350b | 479 | assert_return(!m->sealed, -EPERM); |
| 0a0dc69b | 480 | assert_return(data, -EINVAL); |
| 67b19a49 | 481 | assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); |
| 0a0dc69b | 482 | |
| ebf404a4 | 483 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_IN_ADDR); |
| 0a0dc69b TG |
484 | if (r < 0) |
| 485 | return r; | |
| 486 | ||
| ebf404a4 | 487 | r = add_rtattr(m, attr_type, data, FAMILY_ADDRESS_SIZE(family)); |
| 0a0dc69b TG |
488 | if (r < 0) |
| 489 | return r; | |
| 490 | ||
| 491 | return 0; | |
| 492 | } | |
| 493 | ||
| dd35a61c | 494 | int sd_netlink_message_append_in_addr(sd_netlink_message *m, uint16_t attr_type, const struct in_addr *data) { |
| ebf404a4 | 495 | return netlink_message_append_in_addr_union(m, attr_type, AF_INET, (const union in_addr_union *) data); |
| 67b19a49 | 496 | } |
| abd48ec8 | 497 | |
| dd35a61c | 498 | int sd_netlink_message_append_in6_addr(sd_netlink_message *m, uint16_t attr_type, const struct in6_addr *data) { |
| ebf404a4 | 499 | return netlink_message_append_in_addr_union(m, attr_type, AF_INET6, (const union in_addr_union *) data); |
| abd48ec8 YW |
500 | } |
| 501 | ||
| ebf404a4 | 502 | int netlink_message_append_sockaddr_union(sd_netlink_message *m, uint16_t attr_type, const union sockaddr_union *data) { |
| abd48ec8 YW |
503 | int r; |
| 504 | ||
| 505 | assert_return(m, -EINVAL); | |
| 506 | assert_return(!m->sealed, -EPERM); | |
| 507 | assert_return(data, -EINVAL); | |
| 67b19a49 | 508 | assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL); |
| abd48ec8 | 509 | |
| ebf404a4 | 510 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_SOCKADDR); |
| abd48ec8 YW |
511 | if (r < 0) |
| 512 | return r; | |
| 513 | ||
| ebf404a4 | 514 | r = add_rtattr(m, attr_type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); |
| abd48ec8 YW |
515 | if (r < 0) |
| 516 | return r; | |
| 517 | ||
| 518 | return 0; | |
| 519 | } | |
| 520 | ||
| dd35a61c | 521 | int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in *data) { |
| ebf404a4 | 522 | return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data); |
| 67b19a49 | 523 | } |
| 0a0dc69b | 524 | |
| dd35a61c | 525 | int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in6 *data) { |
| ebf404a4 | 526 | return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data); |
| 0a0dc69b TG |
527 | } |
| 528 | ||
| dd35a61c | 529 | int sd_netlink_message_append_ether_addr(sd_netlink_message *m, uint16_t attr_type, const struct ether_addr *data) { |
| 0a0dc69b TG |
530 | int r; |
| 531 | ||
| 532 | assert_return(m, -EINVAL); | |
| e5c4350b | 533 | assert_return(!m->sealed, -EPERM); |
| 0a0dc69b TG |
534 | assert_return(data, -EINVAL); |
| 535 | ||
| ebf404a4 | 536 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR); |
| d8e538ec TG |
537 | if (r < 0) |
| 538 | return r; | |
| 0a0dc69b | 539 | |
| ebf404a4 | 540 | r = add_rtattr(m, attr_type, data, ETH_ALEN); |
| 0a0dc69b TG |
541 | if (r < 0) |
| 542 | return r; | |
| 543 | ||
| 544 | return 0; | |
| 65f568bb TG |
545 | } |
| 546 | ||
| ebf404a4 | 547 | int netlink_message_append_hw_addr(sd_netlink_message *m, uint16_t attr_type, const struct hw_addr_data *data) { |
| 4fc8a29a TR |
548 | int r; |
| 549 | ||
| 550 | assert_return(m, -EINVAL); | |
| 551 | assert_return(!m->sealed, -EPERM); | |
| 552 | assert_return(data, -EINVAL); | |
| 553 | assert_return(data->length > 0, -EINVAL); | |
| 554 | ||
| ebf404a4 | 555 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR); |
| 4fc8a29a TR |
556 | if (r < 0) |
| 557 | return r; | |
| 558 | ||
| ebf404a4 | 559 | r = add_rtattr(m, attr_type, data->bytes, data->length); |
| 4fc8a29a TR |
560 | if (r < 0) |
| 561 | return r; | |
| 562 | ||
| 563 | return 0; | |
| 564 | } | |
| 565 | ||
| dd35a61c | 566 | int sd_netlink_message_append_cache_info(sd_netlink_message *m, uint16_t attr_type, const struct ifa_cacheinfo *info) { |
| aba496a5 UTL |
567 | int r; |
| 568 | ||
| 569 | assert_return(m, -EINVAL); | |
| 570 | assert_return(!m->sealed, -EPERM); | |
| 571 | assert_return(info, -EINVAL); | |
| 572 | ||
| ebf404a4 | 573 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_CACHE_INFO); |
| aba496a5 UTL |
574 | if (r < 0) |
| 575 | return r; | |
| 576 | ||
| ebf404a4 | 577 | r = add_rtattr(m, attr_type, info, sizeof(struct ifa_cacheinfo)); |
| aba496a5 UTL |
578 | if (r < 0) |
| 579 | return r; | |
| 580 | ||
| 581 | return 0; | |
| 582 | } | |
| 583 | ||
| dd35a61c | 584 | int sd_netlink_message_open_container(sd_netlink_message *m, uint16_t attr_type) { |
| d8e538ec TG |
585 | size_t size; |
| 586 | int r; | |
| 33125ac5 | 587 | |
| 65f568bb | 588 | assert_return(m, -EINVAL); |
| e5c4350b | 589 | assert_return(!m->sealed, -EPERM); |
| 179b4db4 | 590 | /* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */ |
| 409856d3 | 591 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
| 33125ac5 | 592 | |
| ebf404a4 | 593 | r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED); |
| 4af7b60d | 594 | if (r < 0) { |
| ebf404a4 | 595 | const NLAPolicySetUnion *policy_set_union; |
| 4af7b60d TG |
596 | int family; |
| 597 | ||
| ebf404a4 | 598 | r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED_UNION_BY_FAMILY); |
| 4af7b60d TG |
599 | if (r < 0) |
| 600 | return r; | |
| 4af7b60d | 601 | |
| 89489ef7 | 602 | r = sd_rtnl_message_get_family(m, &family); |
| 4af7b60d TG |
603 | if (r < 0) |
| 604 | return r; | |
| 605 | ||
| ebf404a4 YW |
606 | policy_set_union = policy_set_get_policy_set_union( |
| 607 | m->containers[m->n_containers].policy_set, | |
| 608 | attr_type); | |
| 609 | if (!policy_set_union) | |
| 98be4292 | 610 | return -EOPNOTSUPP; |
| 4af7b60d | 611 | |
| ebf404a4 YW |
612 | m->containers[m->n_containers + 1].policy_set = |
| 613 | policy_set_union_get_policy_set_by_family( | |
| 614 | policy_set_union, | |
| ea073c8f | 615 | family); |
| 98be4292 | 616 | } else |
| ebf404a4 YW |
617 | m->containers[m->n_containers + 1].policy_set = |
| 618 | policy_set_get_policy_set( | |
| 619 | m->containers[m->n_containers].policy_set, | |
| 620 | attr_type); | |
| 621 | if (!m->containers[m->n_containers + 1].policy_set) | |
| 98be4292 | 622 | return -EOPNOTSUPP; |
| 31a4e153 | 623 | |
| ebf404a4 | 624 | r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, size); |
| d8e538ec TG |
625 | if (r < 0) |
| 626 | return r; | |
| 627 | ||
| 313cefa1 | 628 | m->containers[m->n_containers++].offset = r; |
| 7ca1d319 | 629 | |
| d8e538ec TG |
630 | return 0; |
| 631 | } | |
| 632 | ||
| dd35a61c | 633 | int sd_netlink_message_open_container_union(sd_netlink_message *m, uint16_t attr_type, const char *key) { |
| ebf404a4 | 634 | const NLAPolicySetUnion *policy_set_union; |
| d8e538ec TG |
635 | int r; |
| 636 | ||
| 637 | assert_return(m, -EINVAL); | |
| 638 | assert_return(!m->sealed, -EPERM); | |
| 409856d3 | 639 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
| d8e538ec | 640 | |
| ebf404a4 YW |
641 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_NESTED_UNION_BY_STRING); |
| 642 | if (r < 0) | |
| 643 | return r; | |
| 644 | ||
| 645 | policy_set_union = policy_set_get_policy_set_union( | |
| 646 | m->containers[m->n_containers].policy_set, | |
| 647 | attr_type); | |
| 648 | if (!policy_set_union) | |
| 98be4292 | 649 | return -EOPNOTSUPP; |
| d8e538ec | 650 | |
| ebf404a4 YW |
651 | m->containers[m->n_containers + 1].policy_set = |
| 652 | policy_set_union_get_policy_set_by_string( | |
| 653 | policy_set_union, | |
| ea073c8f | 654 | key); |
| ebf404a4 | 655 | if (!m->containers[m->n_containers + 1].policy_set) |
| 98be4292 | 656 | return -EOPNOTSUPP; |
| 33125ac5 | 657 | |
| ebf404a4 | 658 | r = sd_netlink_message_append_string(m, policy_set_union_get_match_attribute(policy_set_union), key); |
| d8e538ec TG |
659 | if (r < 0) |
| 660 | return r; | |
| 661 | ||
| 05d0c2e3 | 662 | /* do we ever need non-null size */ |
| ebf404a4 | 663 | r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0); |
| d8e538ec TG |
664 | if (r < 0) |
| 665 | return r; | |
| 666 | ||
| 313cefa1 | 667 | m->containers[m->n_containers++].offset = r; |
| 7ca1d319 | 668 | |
| d8e538ec | 669 | return 0; |
| 33125ac5 TG |
670 | } |
| 671 | ||
| dd35a61c | 672 | int sd_netlink_message_close_container(sd_netlink_message *m) { |
| 33125ac5 | 673 | assert_return(m, -EINVAL); |
| e5c4350b | 674 | assert_return(!m->sealed, -EPERM); |
| 5a081409 | 675 | assert_return(m->n_containers > 0, -EINVAL); |
| 33125ac5 | 676 | |
| ebf404a4 | 677 | m->containers[m->n_containers].policy_set = NULL; |
| 05d0c2e3 JT |
678 | m->containers[m->n_containers].offset = 0; |
| 679 | m->n_containers--; | |
| 680 | ||
| 681 | return 0; | |
| 682 | } | |
| 683 | ||
| dd35a61c | 684 | int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t attr_type) { |
| 05d0c2e3 JT |
685 | int r; |
| 686 | ||
| 687 | assert_return(m, -EINVAL); | |
| 688 | assert_return(!m->sealed, -EPERM); | |
| 409856d3 | 689 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
| 05d0c2e3 | 690 | |
| ebf404a4 | 691 | r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0); |
| 05d0c2e3 JT |
692 | if (r < 0) |
| 693 | return r; | |
| 694 | ||
| 695 | m->containers[m->n_containers].offset = r; | |
| 696 | m->n_containers++; | |
| ebf404a4 | 697 | m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set; |
| 05d0c2e3 JT |
698 | |
| 699 | return 0; | |
| 700 | } | |
| 701 | ||
| dd35a61c | 702 | int sd_netlink_message_cancel_array(sd_netlink_message *m) { |
| 05d0c2e3 JT |
703 | uint32_t rta_len; |
| 704 | ||
| 705 | assert_return(m, -EINVAL); | |
| 706 | assert_return(!m->sealed, -EPERM); | |
| 707 | assert_return(m->n_containers > 1, -EINVAL); | |
| 708 | ||
| 709 | rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len; | |
| 710 | ||
| c7209bcf | 711 | for (unsigned i = 0; i < m->n_containers; i++) |
| 05d0c2e3 JT |
712 | GET_CONTAINER(m, i)->rta_len -= rta_len; |
| 713 | ||
| 714 | m->hdr->nlmsg_len -= rta_len; | |
| 715 | ||
| 313cefa1 | 716 | m->n_containers--; |
| ebf404a4 | 717 | m->containers[m->n_containers].policy_set = NULL; |
| 33125ac5 TG |
718 | |
| 719 | return 0; | |
| 720 | } | |
| 721 | ||
| 3b4e3ebb YW |
722 | static int netlink_message_read_internal( |
| 723 | sd_netlink_message *m, | |
| ebf404a4 | 724 | uint16_t attr_type, |
| 3b4e3ebb YW |
725 | void **ret_data, |
| 726 | bool *ret_net_byteorder) { | |
| 727 | ||
| f663aeb8 | 728 | struct netlink_attribute *attribute; |
| f66eeb6b TG |
729 | struct rtattr *rta; |
| 730 | ||
| 44caa5e7 SS |
731 | assert_return(m, -EINVAL); |
| 732 | assert_return(m->sealed, -EPERM); | |
| f1dd72c2 | 733 | |
| 409856d3 | 734 | assert(m->n_containers < NETLINK_CONTAINER_DEPTH); |
| e4a1e68d YW |
735 | |
| 736 | if (!m->containers[m->n_containers].attributes) | |
| 737 | return -ENODATA; | |
| 48fb0d13 | 738 | |
| ebf404a4 | 739 | if (attr_type > m->containers[m->n_containers].max_attribute) |
| 48fb0d13 | 740 | return -ENODATA; |
| 44caa5e7 | 741 | |
| ebf404a4 | 742 | attribute = &m->containers[m->n_containers].attributes[attr_type]; |
| f663aeb8 | 743 | |
| f1dd72c2 | 744 | if (attribute->offset == 0) |
| 44caa5e7 SS |
745 | return -ENODATA; |
| 746 | ||
| f663aeb8 | 747 | rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); |
| 44caa5e7 | 748 | |
| 3b4e3ebb YW |
749 | if (ret_data) |
| 750 | *ret_data = RTA_DATA(rta); | |
| f66eeb6b | 751 | |
| 3b4e3ebb YW |
752 | if (ret_net_byteorder) |
| 753 | *ret_net_byteorder = attribute->net_byteorder; | |
| 4c641e99 | 754 | |
| f66eeb6b | 755 | return RTA_PAYLOAD(rta); |
| 44caa5e7 SS |
756 | } |
| 757 | ||
| 2fcaeaaa YW |
758 | static int netlink_message_read_impl( |
| 759 | sd_netlink_message *m, | |
| 760 | uint16_t attr_type, | |
| 761 | bool strict, | |
| 762 | NLAType type, | |
| 763 | size_t size, | |
| 764 | void *ret, | |
| 765 | bool *ret_net_byteorder) { | |
| 766 | ||
| 767 | bool net_byteorder; | |
| 926062f0 SS |
768 | void *attr_data; |
| 769 | int r; | |
| 770 | ||
| 2fcaeaaa | 771 | assert(m); |
| 926062f0 | 772 | |
| 2fcaeaaa YW |
773 | if (type >= 0) { |
| 774 | r = message_attribute_has_type(m, NULL, attr_type, type); | |
| 775 | if (r < 0) | |
| 776 | return r; | |
| 777 | } | |
| 778 | ||
| 779 | r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder); | |
| 926062f0 SS |
780 | if (r < 0) |
| 781 | return r; | |
| 782 | ||
| 7d9337ed ZJS |
783 | if ((size_t) r > size) |
| 784 | return -ENOBUFS; | |
| 926062f0 | 785 | |
| 2fcaeaaa YW |
786 | if (strict && (size_t) r != size) |
| 787 | return -EIO; | |
| 788 | ||
| ae298c93 | 789 | if (ret) |
| 2fcaeaaa YW |
790 | memzero(mempcpy(ret, attr_data, r), size - (size_t) r); |
| 791 | ||
| 792 | if (ret_net_byteorder) | |
| 793 | *ret_net_byteorder = net_byteorder; | |
| 926062f0 | 794 | |
| 5dc3dbe8 | 795 | return r; |
| 926062f0 SS |
796 | } |
| 797 | ||
| 2fcaeaaa YW |
798 | int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *ret) { |
| 799 | assert_return(m, -EINVAL); | |
| 800 | ||
| 801 | return netlink_message_read_impl( | |
| 802 | m, attr_type, /* strict = */ false, | |
| 803 | _NETLINK_TYPE_INVALID, size, | |
| 804 | ret, /* ret_net_byteorder = */ NULL); | |
| 805 | } | |
| 806 | ||
| dd35a61c | 807 | int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) { |
| 52888279 | 808 | void *attr_data; |
| de52a83c YW |
809 | int r; |
| 810 | ||
| 811 | assert_return(m, -EINVAL); | |
| 812 | ||
| ebf404a4 | 813 | r = netlink_message_read_internal(m, attr_type, &attr_data, NULL); |
| a1d2ae06 YW |
814 | if (r < 0) |
| 815 | return r; | |
| 816 | ||
| 817 | if (ret_data) { | |
| 818 | void *data; | |
| 819 | ||
| 820 | data = memdup_suffix0(attr_data, r); | |
| de52a83c YW |
821 | if (!data) |
| 822 | return -ENOMEM; | |
| 823 | ||
| 824 | *ret_data = data; | |
| 825 | } | |
| 826 | ||
| 827 | if (ret_size) | |
| 828 | *ret_size = r; | |
| 829 | ||
| 830 | return r; | |
| 831 | } | |
| 832 | ||
| ae298c93 | 833 | int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **ret) { |
| 768237e7 | 834 | const char *s; |
| 59d4103f YW |
835 | int r; |
| 836 | ||
| 837 | assert_return(m, -EINVAL); | |
| 838 | ||
| 768237e7 | 839 | r = sd_netlink_message_read_string(m, attr_type, &s); |
| 59d4103f YW |
840 | if (r < 0) |
| 841 | return r; | |
| 842 | ||
| 768237e7 | 843 | return strdup_to(ret, s); |
| 59d4103f YW |
844 | } |
| 845 | ||
| ae298c93 | 846 | int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **ret) { |
| 44caa5e7 | 847 | void *attr_data; |
| 52888279 | 848 | int r; |
| 44caa5e7 | 849 | |
| 73ae2b7d TG |
850 | assert_return(m, -EINVAL); |
| 851 | ||
| ebf404a4 | 852 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_STRING); |
| d8e538ec TG |
853 | if (r < 0) |
| 854 | return r; | |
| 44caa5e7 | 855 | |
| ebf404a4 | 856 | r = netlink_message_read_internal(m, attr_type, &attr_data, NULL); |
| f66eeb6b | 857 | if (r < 0) |
| 44caa5e7 | 858 | return r; |
| 52888279 YW |
859 | |
| 860 | if (strnlen(attr_data, r) >= (size_t) r) | |
| f66eeb6b | 861 | return -EIO; |
| 44caa5e7 | 862 | |
| ae298c93 YW |
863 | if (ret) |
| 864 | *ret = (const char *) attr_data; | |
| 44caa5e7 | 865 | |
| 08fd159d | 866 | return r; |
| 44caa5e7 SS |
867 | } |
| 868 | ||
| ae298c93 | 869 | int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *ret) { |
| 73ae2b7d TG |
870 | assert_return(m, -EINVAL); |
| 871 | ||
| 2fcaeaaa YW |
872 | return netlink_message_read_impl( |
| 873 | m, attr_type, /* strict = */ true, | |
| 874 | NETLINK_TYPE_U8, sizeof(uint8_t), | |
| 875 | ret, /* ret_net_byteorder = */ NULL); | |
| 44caa5e7 SS |
876 | } |
| 877 | ||
| ae298c93 | 878 | int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *ret) { |
| 4c641e99 | 879 | bool net_byteorder; |
| 2fcaeaaa | 880 | uint16_t u; |
| 4c641e99 | 881 | int r; |
| 44caa5e7 | 882 | |
| 73ae2b7d TG |
883 | assert_return(m, -EINVAL); |
| 884 | ||
| 2fcaeaaa YW |
885 | r = netlink_message_read_impl( |
| 886 | m, attr_type, /* strict = */ true, | |
| 887 | NETLINK_TYPE_U16, sizeof(uint16_t), | |
| 888 | ret ? &u : NULL, &net_byteorder); | |
| d8e538ec TG |
889 | if (r < 0) |
| 890 | return r; | |
| 44caa5e7 | 891 | |
| 2fcaeaaa YW |
892 | if (ret) |
| 893 | *ret = net_byteorder ? be16toh(u) : u; | |
| 44caa5e7 SS |
894 | |
| 895 | return 0; | |
| 896 | } | |
| 897 | ||
| ae298c93 | 898 | int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *ret) { |
| 4c641e99 | 899 | bool net_byteorder; |
| 2fcaeaaa | 900 | uint32_t u; |
| 4c641e99 | 901 | int r; |
| 44caa5e7 | 902 | |
| 73ae2b7d TG |
903 | assert_return(m, -EINVAL); |
| 904 | ||
| 2fcaeaaa YW |
905 | r = netlink_message_read_impl( |
| 906 | m, attr_type, /* strict = */ true, | |
| 907 | NETLINK_TYPE_U32, sizeof(uint32_t), | |
| 908 | ret ? &u : NULL, &net_byteorder); | |
| f66eeb6b | 909 | if (r < 0) |
| 44caa5e7 | 910 | return r; |
| 52888279 | 911 | |
| 2fcaeaaa YW |
912 | if (ret) |
| 913 | *ret = net_byteorder ? be32toh(u) : u; | |
| 44caa5e7 SS |
914 | |
| 915 | return 0; | |
| 916 | } | |
| 917 | ||
| 72757397 | 918 | int sd_netlink_message_read_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t *ret) { |
| 72757397 | 919 | bool net_byteorder; |
| 2fcaeaaa | 920 | uint64_t u; |
| 72757397 YW |
921 | int r; |
| 922 | ||
| 923 | assert_return(m, -EINVAL); | |
| 924 | ||
| 2fcaeaaa YW |
925 | r = netlink_message_read_impl( |
| 926 | m, attr_type, /* strict = */ true, | |
| 927 | NETLINK_TYPE_U64, sizeof(uint64_t), | |
| 928 | ret ? &u : NULL, &net_byteorder); | |
| 72757397 YW |
929 | if (r < 0) |
| 930 | return r; | |
| 931 | ||
| 2fcaeaaa YW |
932 | if (ret) |
| 933 | *ret = net_byteorder ? be64toh(u) : u; | |
| 72757397 YW |
934 | |
| 935 | return 0; | |
| 936 | } | |
| 937 | ||
| ae298c93 | 938 | int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *ret) { |
| 73ae2b7d TG |
939 | assert_return(m, -EINVAL); |
| 940 | ||
| 2fcaeaaa YW |
941 | return netlink_message_read_impl( |
| 942 | m, attr_type, /* strict = */ true, | |
| 943 | NETLINK_TYPE_ETHER_ADDR, sizeof(struct ether_addr), | |
| 944 | ret, /* ret_net_byteorder = */ NULL); | |
| 4e9e7f18 SS |
945 | } |
| 946 | ||
| ae298c93 | 947 | int netlink_message_read_hw_addr(sd_netlink_message *m, uint16_t attr_type, struct hw_addr_data *ret) { |
| 52888279 | 948 | int r; |
| 4fc8a29a TR |
949 | |
| 950 | assert_return(m, -EINVAL); | |
| 951 | ||
| 2fcaeaaa YW |
952 | r = netlink_message_read_impl( |
| 953 | m, attr_type, /* strict = */ false, | |
| 954 | NETLINK_TYPE_ETHER_ADDR, HW_ADDR_MAX_SIZE, | |
| 955 | ret ? ret->bytes : NULL, /* ret_net_byteorder = */ NULL); | |
| 4fc8a29a TR |
956 | if (r < 0) |
| 957 | return r; | |
| 52888279 | 958 | |
| 2fcaeaaa | 959 | if (ret) |
| ae298c93 | 960 | ret->length = r; |
| 4fc8a29a | 961 | |
| 2fcaeaaa | 962 | return r; |
| 4fc8a29a TR |
963 | } |
| 964 | ||
| ae298c93 | 965 | int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *ret) { |
| 73ae2b7d TG |
966 | assert_return(m, -EINVAL); |
| 967 | ||
| 2fcaeaaa YW |
968 | return netlink_message_read_impl( |
| 969 | m, attr_type, /* strict = */ true, | |
| 970 | NETLINK_TYPE_CACHE_INFO, sizeof(struct ifa_cacheinfo), | |
| 971 | ret, /* ret_net_byteorder = */ NULL); | |
| aba496a5 UTL |
972 | } |
| 973 | ||
| ae298c93 | 974 | int netlink_message_read_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, union in_addr_union *ret) { |
| f29b6b37 | 975 | int r; |
| 4e9e7f18 | 976 | |
| 73ae2b7d | 977 | assert_return(m, -EINVAL); |
| f29b6b37 | 978 | assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); |
| 73ae2b7d | 979 | |
| 2fcaeaaa YW |
980 | r = netlink_message_read_impl( |
| 981 | m, attr_type, /* strict = */ true, | |
| 982 | NETLINK_TYPE_IN_ADDR, FAMILY_ADDRESS_SIZE(family), | |
| 983 | ret, /* ret_net_byteorder = */ NULL); | |
| f66eeb6b | 984 | if (r < 0) |
| 4e9e7f18 | 985 | return r; |
| 52888279 | 986 | |
| ae298c93 | 987 | if (ret) |
| 2fcaeaaa | 988 | memzero((uint8_t*) ret + FAMILY_ADDRESS_SIZE(family), sizeof(union in_addr_union) - FAMILY_ADDRESS_SIZE(family)); |
| 4e9e7f18 | 989 | |
| 2fcaeaaa | 990 | return r; |
| 4e9e7f18 SS |
991 | } |
| 992 | ||
| ae298c93 | 993 | int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *ret) { |
| 2fcaeaaa | 994 | assert_return(m, -EINVAL); |
| 73ae2b7d | 995 | |
| 2fcaeaaa YW |
996 | return netlink_message_read_impl( |
| 997 | m, attr_type, /* strict = */ true, | |
| 998 | NETLINK_TYPE_IN_ADDR, sizeof(struct in_addr), | |
| 999 | ret, /* ret_net_byteorder = */ NULL); | |
| f29b6b37 | 1000 | } |
| 4e9e7f18 | 1001 | |
| ae298c93 | 1002 | int sd_netlink_message_read_in6_addr(sd_netlink_message *m, uint16_t attr_type, struct in6_addr *ret) { |
| 2fcaeaaa | 1003 | assert_return(m, -EINVAL); |
| 4e9e7f18 | 1004 | |
| 2fcaeaaa YW |
1005 | return netlink_message_read_impl( |
| 1006 | m, attr_type, /* strict = */ true, | |
| 1007 | NETLINK_TYPE_IN_ADDR, sizeof(struct in6_addr), | |
| 1008 | ret, /* ret_net_byteorder = */ NULL); | |
| 4e9e7f18 SS |
1009 | } |
| 1010 | ||
| dd35a61c | 1011 | int sd_netlink_message_has_flag(sd_netlink_message *m, uint16_t attr_type) { |
| 3b4e3ebb YW |
1012 | void *attr_data; |
| 1013 | int r; | |
| 1014 | ||
| 1015 | assert_return(m, -EINVAL); | |
| 1016 | ||
| 1017 | /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */ | |
| 1018 | ||
| ebf404a4 | 1019 | r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_FLAG); |
| 3b4e3ebb YW |
1020 | if (r < 0) |
| 1021 | return r; | |
| 1022 | ||
| ebf404a4 | 1023 | r = netlink_message_read_internal(m, attr_type, &attr_data, NULL); |
| 3b4e3ebb YW |
1024 | if (r == -ENODATA) |
| 1025 | return 0; | |
| 1026 | if (r < 0) | |
| 1027 | return r; | |
| 1028 | ||
| 1029 | return 1; | |
| 1030 | } | |
| 1031 | ||
| dd35a61c | 1032 | int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret) { |
| 8f3c1859 | 1033 | _cleanup_strv_free_ char **s = NULL; |
| ebf404a4 YW |
1034 | const NLAPolicySet *policy_set; |
| 1035 | const NLAPolicy *policy; | |
| 8f3c1859 YW |
1036 | struct rtattr *rta; |
| 1037 | void *container; | |
| f501c251 | 1038 | size_t rt_len; |
| 8f3c1859 YW |
1039 | int r; |
| 1040 | ||
| 1041 | assert_return(m, -EINVAL); | |
| 409856d3 | 1042 | assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL); |
| 8f3c1859 | 1043 | |
| ebf404a4 YW |
1044 | policy = policy_set_get_policy( |
| 1045 | m->containers[m->n_containers].policy_set, | |
| 98be4292 | 1046 | container_type); |
| ebf404a4 | 1047 | if (!policy) |
| 98be4292 | 1048 | return -EOPNOTSUPP; |
| 8f3c1859 | 1049 | |
| ebf404a4 | 1050 | if (policy_get_type(policy) != NETLINK_TYPE_NESTED) |
| 8f3c1859 YW |
1051 | return -EINVAL; |
| 1052 | ||
| ebf404a4 YW |
1053 | policy_set = policy_set_get_policy_set( |
| 1054 | m->containers[m->n_containers].policy_set, | |
| 98be4292 | 1055 | container_type); |
| ebf404a4 | 1056 | if (!policy_set) |
| 98be4292 | 1057 | return -EOPNOTSUPP; |
| 8f3c1859 | 1058 | |
| ebf404a4 YW |
1059 | policy = policy_set_get_policy(policy_set, attr_type); |
| 1060 | if (!policy) | |
| 98be4292 | 1061 | return -EOPNOTSUPP; |
| 8f3c1859 | 1062 | |
| ebf404a4 | 1063 | if (policy_get_type(policy) != NETLINK_TYPE_STRING) |
| 8f3c1859 YW |
1064 | return -EINVAL; |
| 1065 | ||
| 1066 | r = netlink_message_read_internal(m, container_type, &container, NULL); | |
| 1067 | if (r < 0) | |
| 1068 | return r; | |
| 1069 | ||
| f501c251 | 1070 | rt_len = (size_t) r; |
| 8f3c1859 YW |
1071 | rta = container; |
| 1072 | ||
| f501c251 YW |
1073 | /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and |
| 1074 | * LGTM.com analysis does not like the type difference. Hence, here we | |
| 1075 | * introduce an unsigned short variable as a workaround. */ | |
| 1076 | unsigned short len = rt_len; | |
| 1077 | for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { | |
| ebf404a4 | 1078 | uint16_t type; |
| 8f3c1859 YW |
1079 | |
| 1080 | type = RTA_TYPE(rta); | |
| ebf404a4 | 1081 | if (type != attr_type) |
| 8f3c1859 YW |
1082 | continue; |
| 1083 | ||
| 1084 | r = strv_extend(&s, RTA_DATA(rta)); | |
| 1085 | if (r < 0) | |
| 1086 | return r; | |
| 1087 | } | |
| 1088 | ||
| 1089 | *ret = TAKE_PTR(s); | |
| 1090 | return 0; | |
| 1091 | } | |
| 1092 | ||
| 416e8419 YW |
1093 | static int netlink_container_parse( |
| 1094 | sd_netlink_message *m, | |
| 1095 | struct netlink_container *container, | |
| 1096 | struct rtattr *rta, | |
| 1097 | size_t rt_len) { | |
| 1098 | ||
| f663aeb8 | 1099 | _cleanup_free_ struct netlink_attribute *attributes = NULL; |
| 416e8419 | 1100 | uint16_t max_attr = 0; |
| 4203fc8b | 1101 | |
| f501c251 YW |
1102 | /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and |
| 1103 | * LGTM.com analysis does not like the type difference. Hence, here we | |
| 1104 | * introduce an unsigned short variable as a workaround. */ | |
| 1105 | unsigned short len = rt_len; | |
| 1106 | for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { | |
| 416e8419 | 1107 | uint16_t attr; |
| f663aeb8 | 1108 | |
| 416e8419 YW |
1109 | attr = RTA_TYPE(rta); |
| 1110 | max_attr = MAX(max_attr, attr); | |
| 4203fc8b | 1111 | |
| 416e8419 | 1112 | if (!GREEDY_REALLOC0(attributes, (size_t) max_attr + 1)) |
| 5fc5e2f5 | 1113 | return -ENOMEM; |
| 4203fc8b | 1114 | |
| 416e8419 | 1115 | if (attributes[attr].offset != 0) |
| 409856d3 | 1116 | log_debug("sd-netlink: message parse - overwriting repeated attribute"); |
| 4203fc8b | 1117 | |
| 416e8419 YW |
1118 | attributes[attr].offset = (uint8_t *) rta - (uint8_t *) m->hdr; |
| 1119 | attributes[attr].nested = RTA_FLAGS(rta) & NLA_F_NESTED; | |
| 1120 | attributes[attr].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; | |
| 4203fc8b TG |
1121 | } |
| 1122 | ||
| ae2a15bc | 1123 | container->attributes = TAKE_PTR(attributes); |
| 416e8419 | 1124 | container->max_attribute = max_attr; |
| 4203fc8b TG |
1125 | |
| 1126 | return 0; | |
| 1127 | } | |
| 1128 | ||
| dd35a61c | 1129 | int sd_netlink_message_enter_container(sd_netlink_message *m, uint16_t attr_type) { |
| ebf404a4 YW |
1130 | const NLAPolicy *policy; |
| 1131 | const NLAPolicySet *policy_set; | |
| 3dd215e0 | 1132 | void *container; |
| d8e538ec TG |
1133 | size_t size; |
| 1134 | int r; | |
| 3dd215e0 TG |
1135 | |
| 1136 | assert_return(m, -EINVAL); | |
| 409856d3 | 1137 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL); |
| 3dd215e0 | 1138 | |
| ebf404a4 YW |
1139 | policy = policy_set_get_policy( |
| 1140 | m->containers[m->n_containers].policy_set, | |
| 1141 | attr_type); | |
| 1142 | if (!policy) | |
| 98be4292 | 1143 | return -EOPNOTSUPP; |
| 3dd215e0 | 1144 | |
| ebf404a4 YW |
1145 | switch (policy_get_type(policy)) { |
| 1146 | case NETLINK_TYPE_NESTED: | |
| 1147 | policy_set = policy_set_get_policy_set( | |
| 1148 | m->containers[m->n_containers].policy_set, | |
| 1149 | attr_type); | |
| 1150 | break; | |
| 1151 | ||
| 1152 | case NETLINK_TYPE_NESTED_UNION_BY_STRING: { | |
| 1153 | const NLAPolicySetUnion *policy_set_union; | |
| 1154 | const char *key; | |
| 817d1cd8 | 1155 | |
| ebf404a4 YW |
1156 | policy_set_union = policy_get_policy_set_union(policy); |
| 1157 | if (!policy_set_union) | |
| 98be4292 | 1158 | return -EOPNOTSUPP; |
| d8e538ec | 1159 | |
| ebf404a4 YW |
1160 | r = sd_netlink_message_read_string( |
| 1161 | m, | |
| 1162 | policy_set_union_get_match_attribute(policy_set_union), | |
| 1163 | &key); | |
| 1164 | if (r < 0) | |
| 1165 | return r; | |
| 1166 | ||
| 1167 | policy_set = policy_set_union_get_policy_set_by_string( | |
| 1168 | policy_set_union, | |
| 1169 | key); | |
| 1170 | break; | |
| 1171 | } | |
| 1172 | case NETLINK_TYPE_NESTED_UNION_BY_FAMILY: { | |
| 1173 | const NLAPolicySetUnion *policy_set_union; | |
| 1174 | int family; | |
| 1175 | ||
| 1176 | policy_set_union = policy_get_policy_set_union(policy); | |
| 1177 | if (!policy_set_union) | |
| 98be4292 | 1178 | return -EOPNOTSUPP; |
| d8e538ec | 1179 | |
| ebf404a4 YW |
1180 | r = sd_rtnl_message_get_family(m, &family); |
| 1181 | if (r < 0) | |
| 1182 | return r; | |
| 1183 | ||
| 1184 | policy_set = policy_set_union_get_policy_set_by_family( | |
| 1185 | policy_set_union, | |
| 1186 | family); | |
| 1187 | break; | |
| 1188 | } | |
| 1189 | default: | |
| 1190 | assert_not_reached(); | |
| 1191 | } | |
| 1192 | if (!policy_set) | |
| 1193 | return -EOPNOTSUPP; | |
| d8e538ec | 1194 | |
| ebf404a4 | 1195 | r = netlink_message_read_internal(m, attr_type, &container, NULL); |
| 3dd215e0 TG |
1196 | if (r < 0) |
| 1197 | return r; | |
| 620fd5d4 | 1198 | |
| 6d185cff | 1199 | size = (size_t) r; |
| 313cefa1 | 1200 | m->n_containers++; |
| 3dd215e0 | 1201 | |
| f663aeb8 TG |
1202 | r = netlink_container_parse(m, |
| 1203 | &m->containers[m->n_containers], | |
| f663aeb8 TG |
1204 | container, |
| 1205 | size); | |
| d8e538ec | 1206 | if (r < 0) { |
| 313cefa1 | 1207 | m->n_containers--; |
| 3dd215e0 | 1208 | return r; |
| d8e538ec | 1209 | } |
| 3dd215e0 | 1210 | |
| ebf404a4 | 1211 | m->containers[m->n_containers].policy_set = policy_set; |
| 3dd215e0 TG |
1212 | |
| 1213 | return 0; | |
| 1214 | } | |
| 1215 | ||
| dd35a61c | 1216 | int sd_netlink_message_enter_array(sd_netlink_message *m, uint16_t attr_type) { |
| 5fc5e2f5 YW |
1217 | void *container; |
| 1218 | size_t size; | |
| 1219 | int r; | |
| 1220 | ||
| 1221 | assert_return(m, -EINVAL); | |
| 409856d3 | 1222 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL); |
| 5fc5e2f5 | 1223 | |
| ebf404a4 | 1224 | r = netlink_message_read_internal(m, attr_type, &container, NULL); |
| 5fc5e2f5 YW |
1225 | if (r < 0) |
| 1226 | return r; | |
| 1227 | ||
| 1228 | size = (size_t) r; | |
| 5fc5e2f5 YW |
1229 | m->n_containers++; |
| 1230 | ||
| 1231 | r = netlink_container_parse(m, | |
| 1232 | &m->containers[m->n_containers], | |
| 1233 | container, | |
| 1234 | size); | |
| 1235 | if (r < 0) { | |
| 1236 | m->n_containers--; | |
| 1237 | return r; | |
| 1238 | } | |
| 1239 | ||
| ebf404a4 | 1240 | m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set; |
| 5fc5e2f5 YW |
1241 | |
| 1242 | return 0; | |
| 1243 | } | |
| 1244 | ||
| dd35a61c | 1245 | int sd_netlink_message_exit_container(sd_netlink_message *m) { |
| e5c4350b TG |
1246 | assert_return(m, -EINVAL); |
| 1247 | assert_return(m->sealed, -EINVAL); | |
| 1248 | assert_return(m->n_containers > 0, -EINVAL); | |
| 1249 | ||
| a1e58e8e | 1250 | m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); |
| 416e8419 | 1251 | m->containers[m->n_containers].max_attribute = 0; |
| ebf404a4 | 1252 | m->containers[m->n_containers].policy_set = NULL; |
| 3dd215e0 | 1253 | |
| 313cefa1 | 1254 | m->n_containers--; |
| e5c4350b TG |
1255 | |
| 1256 | return 0; | |
| 1257 | } | |
| 1258 | ||
| dd35a61c | 1259 | int sd_netlink_message_get_max_attribute(sd_netlink_message *m, uint16_t *ret) { |
| 416e8419 YW |
1260 | assert_return(m, -EINVAL); |
| 1261 | assert_return(m->sealed, -EINVAL); | |
| 1262 | assert_return(ret, -EINVAL); | |
| 1263 | ||
| 1264 | *ret = m->containers[m->n_containers].max_attribute; | |
| 1265 | return 0; | |
| 1266 | } | |
| 1267 | ||
| dd35a61c | 1268 | int sd_netlink_message_is_error(sd_netlink_message *m) { |
| 45af44d4 TG |
1269 | assert_return(m, 0); |
| 1270 | assert_return(m->hdr, 0); | |
| 1271 | ||
| 1272 | return m->hdr->nlmsg_type == NLMSG_ERROR; | |
| 1273 | } | |
| 1274 | ||
| dd35a61c | 1275 | int sd_netlink_message_get_errno(sd_netlink_message *m) { |
| 65f568bb TG |
1276 | struct nlmsgerr *err; |
| 1277 | ||
| e16bcf98 | 1278 | assert_return(m, -EINVAL); |
| 9d0db178 | 1279 | assert_return(m->hdr, -EINVAL); |
| 65f568bb | 1280 | |
| 1c4baffc | 1281 | if (!sd_netlink_message_is_error(m)) |
| 65f568bb TG |
1282 | return 0; |
| 1283 | ||
| 1284 | err = NLMSG_DATA(m->hdr); | |
| 1285 | ||
| 1286 | return err->error; | |
| 1287 | } | |
| 1288 | ||
| e4a1e68d YW |
1289 | static int netlink_message_parse_error(sd_netlink_message *m) { |
| 1290 | struct nlmsgerr *err = NLMSG_DATA(m->hdr); | |
| 1291 | size_t hlen = sizeof(struct nlmsgerr); | |
| 1292 | ||
| 1293 | /* no TLVs, nothing to do here */ | |
| 1294 | if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS)) | |
| 1295 | return 0; | |
| 1296 | ||
| 1297 | /* if NLM_F_CAPPED is set then the inner err msg was capped */ | |
| 1298 | if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED)) | |
| 1299 | hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr); | |
| 1300 | ||
| 1301 | if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen)) | |
| 1302 | return 0; | |
| 1303 | ||
| 1304 | return netlink_container_parse(m, | |
| 1305 | &m->containers[m->n_containers], | |
| 1306 | (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen), | |
| 1307 | NLMSG_PAYLOAD(m->hdr, hlen)); | |
| 1308 | } | |
| 1309 | ||
| dd35a61c | 1310 | int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) { |
| 817d1cd8 | 1311 | size_t size; |
| 3dd215e0 | 1312 | int r; |
| 0fc7531b TG |
1313 | |
| 1314 | assert_return(m, -EINVAL); | |
| aee6309b | 1315 | assert_return(nl, -EINVAL); |
| 0fc7531b | 1316 | |
| 3dd215e0 | 1317 | /* don't allow appending to message once parsed */ |
| c737abd3 | 1318 | message_seal(m); |
| 3dd215e0 | 1319 | |
| c7209bcf | 1320 | for (unsigned i = 1; i <= m->n_containers; i++) |
| a1e58e8e | 1321 | m->containers[i].attributes = mfree(m->containers[i].attributes); |
| 3dd215e0 TG |
1322 | |
| 1323 | m->n_containers = 0; | |
| 1324 | ||
| ece174c5 | 1325 | if (m->containers[0].attributes) |
| 3dd215e0 TG |
1326 | /* top-level attributes have already been parsed */ |
| 1327 | return 0; | |
| 3dd215e0 | 1328 | |
| d8e538ec TG |
1329 | assert(m->hdr); |
| 1330 | ||
| 6e196011 LP |
1331 | r = netlink_get_policy_set_and_header_size( |
| 1332 | nl, | |
| 1333 | m->hdr->nlmsg_type, | |
| 1334 | m->hdr->nlmsg_flags, | |
| 1335 | &m->containers[0].policy_set, | |
| 1336 | &size); | |
| d8e538ec TG |
1337 | if (r < 0) |
| 1338 | return r; | |
| 1339 | ||
| 56fdc16d YW |
1340 | if (sd_netlink_message_is_error(m)) |
| 1341 | return netlink_message_parse_error(m); | |
| d8e538ec | 1342 | |
| 56fdc16d YW |
1343 | return netlink_container_parse(m, |
| 1344 | &m->containers[0], | |
| 1345 | (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), | |
| 1346 | NLMSG_PAYLOAD(m->hdr, size)); | |
| 0fc7531b | 1347 | } |
| 3dd215e0 | 1348 | |
| 409856d3 | 1349 | void message_seal(sd_netlink_message *m) { |
| 3dd215e0 | 1350 | assert(m); |
| 3dd215e0 TG |
1351 | |
| 1352 | m->sealed = true; | |
| 1353 | } | |
| 1403f45a | 1354 | |
| ae298c93 | 1355 | sd_netlink_message* sd_netlink_message_next(sd_netlink_message *m) { |
| 1403f45a TG |
1356 | assert_return(m, NULL); |
| 1357 | ||
| 1358 | return m->next; | |
| 1359 | } |