dhcp-message: allow to serialize/deserialize sd_dhcp_message object
By using this, we can expose received DHCP message in Describe() DBus/Varlink methods. Preparation for later change.
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
dca9ab5b92
commit
c5997b2bbf
@@ -17,6 +17,7 @@
|
||||
#include "iovec-util.h"
|
||||
#include "iovec-wrapper.h"
|
||||
#include "ip-util.h"
|
||||
#include "json-util.h"
|
||||
#include "network-common.h"
|
||||
#include "set.h"
|
||||
#include "sort-util.h"
|
||||
@@ -1459,3 +1460,188 @@ int dhcp_message_build(sd_dhcp_message *message, struct iovec_wrapper *ret) {
|
||||
*ret = TAKE_STRUCT(iovw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_build_json(sd_dhcp_message *message, sd_json_variant **ret) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(message->header.hlen <= sizeof(message->header.chaddr));
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
r = sd_json_buildo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("op", message->header.op),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("htype", message->header.htype),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("hops", message->header.hops),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("xid", be32toh(message->header.xid)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("secs", be16toh(message->header.secs)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("flags", be16toh(message->header.flags)),
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("ciaddr", &message->header.ciaddr, sizeof(message->header.ciaddr)),
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("yiaddr", &message->header.yiaddr, sizeof(message->header.yiaddr)),
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("siaddr", &message->header.siaddr, sizeof(message->header.siaddr)),
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("giaddr", &message->header.giaddr, sizeof(message->header.giaddr)),
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("chaddr", message->header.chaddr, message->header.hlen));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
uint8_t overload = DHCP_OVERLOAD_NONE;
|
||||
(void) dhcp_message_get_option_u8(message, SD_DHCP_OPTION_OVERLOAD, &overload);
|
||||
|
||||
if (!FLAGS_SET(overload, DHCP_OVERLOAD_SNAME) && !eqzero(message->header.sname)) {
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("sname", message->header.sname, sizeof(message->header.sname)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(overload, DHCP_OVERLOAD_FILE) && !eqzero(message->header.file)) {
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
JSON_BUILD_PAIR_HEX_NON_EMPTY("file", message->header.file, sizeof(message->header.file)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
|
||||
r = tlv_build_json(&message->options, &w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
JSON_BUILD_PAIR_VARIANT_NON_NULL("options", w));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct MessageParam {
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
struct iovec ciaddr;
|
||||
struct iovec yiaddr;
|
||||
struct iovec siaddr;
|
||||
struct iovec giaddr;
|
||||
struct iovec chaddr;
|
||||
struct iovec sname;
|
||||
struct iovec file;
|
||||
TLV *options;
|
||||
} MessageParam;
|
||||
|
||||
static void message_param_done(MessageParam *p) {
|
||||
assert(p);
|
||||
|
||||
iovec_done(&p->ciaddr);
|
||||
iovec_done(&p->yiaddr);
|
||||
iovec_done(&p->siaddr);
|
||||
iovec_done(&p->giaddr);
|
||||
iovec_done(&p->chaddr);
|
||||
iovec_done(&p->sname);
|
||||
iovec_done(&p->file);
|
||||
tlv_unref(p->options);
|
||||
}
|
||||
|
||||
static int dispatch_options(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
TLV **options = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
if (*options)
|
||||
return -EINVAL; /* multiple options field? */
|
||||
|
||||
_cleanup_(tlv_unrefp) TLV *tlv = tlv_new(TLV_DHCP4);
|
||||
if (!tlv)
|
||||
return -ENOMEM;
|
||||
|
||||
r = tlv_parse_json(tlv, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*options = TAKE_PTR(tlv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_parse_json(sd_json_variant *v, sd_dhcp_message **ret) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "op", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(MessageParam, op), SD_JSON_MANDATORY },
|
||||
{ "htype", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(MessageParam, htype), 0 },
|
||||
{ "hops", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(MessageParam, hops), 0 },
|
||||
{ "xid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(MessageParam, xid), 0 },
|
||||
{ "secs", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(MessageParam, secs), 0 },
|
||||
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(MessageParam, flags), 0 },
|
||||
{ "ciaddr", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, ciaddr), 0 },
|
||||
{ "yiaddr", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, yiaddr), 0 },
|
||||
{ "siaddr", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, siaddr), 0 },
|
||||
{ "giaddr", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, giaddr), 0 },
|
||||
{ "chaddr", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, chaddr), 0 },
|
||||
{ "sname", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, sname), 0 },
|
||||
{ "file", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(MessageParam, file), 0 },
|
||||
{ "options", SD_JSON_VARIANT_ARRAY, dispatch_options, offsetof(MessageParam, options), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(message_param_done) MessageParam p = {};
|
||||
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!IN_SET(p.op, BOOTREQUEST, BOOTREPLY))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.ciaddr) || !IN_SET(p.ciaddr.iov_len, 0, sizeof_field(sd_dhcp_message, header.ciaddr)))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.yiaddr) || !IN_SET(p.yiaddr.iov_len, 0, sizeof_field(sd_dhcp_message, header.yiaddr)))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.siaddr) || !IN_SET(p.siaddr.iov_len, 0, sizeof_field(sd_dhcp_message, header.siaddr)))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.giaddr) || !IN_SET(p.giaddr.iov_len, 0, sizeof_field(sd_dhcp_message, header.giaddr)))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.chaddr) || p.chaddr.iov_len > sizeof_field(sd_dhcp_message, header.chaddr))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.sname) || p.sname.iov_len > sizeof_field(sd_dhcp_message, header.sname))
|
||||
return -EINVAL;
|
||||
if (!iovec_is_valid(&p.file) || p.file.iov_len > sizeof_field(sd_dhcp_message, header.file))
|
||||
return -EINVAL;
|
||||
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *message = NULL;
|
||||
r = dhcp_message_new(&message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
message->header = (DHCPMessageHeader) {
|
||||
.op = p.op,
|
||||
.htype = p.htype,
|
||||
.hlen = p.chaddr.iov_len,
|
||||
.hops = p.hops,
|
||||
.xid = htobe32(p.xid),
|
||||
.secs = htobe16(p.secs),
|
||||
.flags = htobe16(p.flags),
|
||||
.magic = htobe32(DHCP_MAGIC_COOKIE),
|
||||
};
|
||||
|
||||
memcpy_safe(&message->header.ciaddr, p.ciaddr.iov_base, p.ciaddr.iov_len);
|
||||
memcpy_safe(&message->header.yiaddr, p.yiaddr.iov_base, p.yiaddr.iov_len);
|
||||
memcpy_safe(&message->header.siaddr, p.siaddr.iov_base, p.siaddr.iov_len);
|
||||
memcpy_safe(&message->header.giaddr, p.giaddr.iov_base, p.giaddr.iov_len);
|
||||
memcpy_safe(message->header.chaddr, p.chaddr.iov_base, p.chaddr.iov_len);
|
||||
memcpy_safe(message->header.sname, p.sname.iov_base, p.sname.iov_len);
|
||||
memcpy_safe(message->header.file, p.file.iov_base, p.file.iov_len);
|
||||
if (p.options) {
|
||||
message->options = TAKE_STRUCT(*p.options);
|
||||
p.options = mfree(p.options);
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -90,3 +90,6 @@ int dhcp_message_parse(
|
||||
sd_dhcp_message **ret);
|
||||
|
||||
int dhcp_message_build(sd_dhcp_message *message, struct iovec_wrapper *ret);
|
||||
|
||||
int dhcp_message_build_json(sd_dhcp_message *message, sd_json_variant **ret);
|
||||
int dhcp_message_parse_json(sd_json_variant *v, sd_dhcp_message **ret);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-client-id-internal.h"
|
||||
#include "dhcp-message.h"
|
||||
@@ -439,6 +441,17 @@ TEST(dhcp_message) {
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw2 = {};
|
||||
ASSERT_OK(dhcp_message_build(m2, &iovw2));
|
||||
ASSERT_TRUE(iovw_equal(&iovw, &iovw2));
|
||||
|
||||
/* json */
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
ASSERT_OK(dhcp_message_build_json(m, &v));
|
||||
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m3 = NULL;
|
||||
ASSERT_OK(dhcp_message_parse_json(v, &m3));
|
||||
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw3 = {};
|
||||
ASSERT_OK(dhcp_message_build(m3, &iovw3));
|
||||
ASSERT_TRUE(iovw_equal(&iovw, &iovw3));
|
||||
}
|
||||
|
||||
static void test_domains_one(size_t len, const uint8_t *data, char * const *expected) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "iovec-util.h"
|
||||
#include "iovec-wrapper.h"
|
||||
#include "json-util.h"
|
||||
#include "tlv-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
@@ -503,3 +504,71 @@ int tlv_build(const TLV *tlv, struct iovec *ret) {
|
||||
*ret = IOVEC_MAKE(TAKE_PTR(buf), sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tlv_build_json(const TLV *tlv, sd_json_variant **ret) {
|
||||
int r;
|
||||
|
||||
assert(tlv);
|
||||
assert(ret);
|
||||
|
||||
/* Sort by tags, for reproducibility. */
|
||||
_cleanup_free_ void **sorted = NULL;
|
||||
size_t n;
|
||||
r = hashmap_dump_keys_sorted(tlv->entries, &sorted, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
FOREACH_ARRAY(tagp, sorted, n) {
|
||||
uint32_t tag = PTR_TO_UINT32(*tagp);
|
||||
struct iovec_wrapper *iovw = ASSERT_PTR(tlv_get_all(tlv, tag));
|
||||
|
||||
FOREACH_ARRAY(iov, iovw->iovec, iovw->count) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("tag", tag),
|
||||
JSON_BUILD_PAIR_IOVEC_HEX("data", iov));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct TLVParam {
|
||||
uint32_t tag;
|
||||
struct iovec data;
|
||||
} TLVParam;
|
||||
|
||||
static void tlv_param_done(TLVParam *p) {
|
||||
iovec_done(&p->data);
|
||||
}
|
||||
|
||||
int tlv_parse_json(TLV *tlv, sd_json_variant *v) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "tag", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(TLVParam, tag), SD_JSON_MANDATORY },
|
||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(TLVParam, data), SD_JSON_MANDATORY },
|
||||
{},
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
assert(tlv);
|
||||
assert(v);
|
||||
|
||||
sd_json_variant *e;
|
||||
JSON_VARIANT_ARRAY_FOREACH(e, v) {
|
||||
_cleanup_(tlv_param_done) TLVParam p = {};
|
||||
r = sd_json_dispatch(e, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = tlv_append(tlv, p.tag, p.data.iov_len, p.data.iov_base);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -80,3 +80,6 @@ int tlv_append_tlv(TLV *tlv, const TLV *source);
|
||||
int tlv_parse(TLV *tlv, const struct iovec *iov);
|
||||
size_t tlv_size(const TLV *tlv);
|
||||
int tlv_build(const TLV *tlv, struct iovec *ret);
|
||||
|
||||
int tlv_build_json(const TLV *tlv, sd_json_variant **ret);
|
||||
int tlv_parse_json(TLV *tlv, sd_json_variant *v);
|
||||
|
||||
Reference in New Issue
Block a user