netlink: fix capped uncapped ack handling in snl(3).
Reviewed by: kp Differential Revision: https://reviews.freebsd.org/D39144 MFC after: 2 weeks
This commit is contained in:
parent
bce30c896d
commit
568a645ba5
@ -388,18 +388,25 @@ snl_parse_attrs(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen,
|
||||
return (snl_parse_attrs_raw(ss, nla_head, len, ps, pslen, target));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
snl_parse_header(struct snl_state *ss, void *hdr, int len,
|
||||
const struct snl_hdr_parser *parser, void *target)
|
||||
static inline void
|
||||
snl_parse_fields(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen __unused,
|
||||
const struct snl_field_parser *ps, int pslen, void *target)
|
||||
{
|
||||
/* Extract fields first (if any) */
|
||||
for (int i = 0; i < parser->fp_size; i++) {
|
||||
const struct snl_field_parser *fp = &parser->fp[i];
|
||||
for (int i = 0; i < pslen; i++) {
|
||||
const struct snl_field_parser *fp = &ps[i];
|
||||
void *src = (char *)hdr + fp->off_in;
|
||||
void *dst = (char *)target + fp->off_out;
|
||||
|
||||
fp->cb(ss, src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
snl_parse_header(struct snl_state *ss, void *hdr, int len,
|
||||
const struct snl_hdr_parser *parser, void *target)
|
||||
{
|
||||
/* Extract fields first (if any) */
|
||||
snl_parse_fields(ss, hdr, parser->hdr_off, parser->fp, parser->fp_size, target);
|
||||
|
||||
struct nlattr *nla_head = (struct nlattr *)(void *)((char *)hdr + parser->hdr_off);
|
||||
bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->hdr_off,
|
||||
@ -575,13 +582,20 @@ snl_field_get_uint32(struct snl_state *ss __unused, void *src, void *target)
|
||||
*((uint32_t *)target) = *((uint32_t *)src);
|
||||
}
|
||||
|
||||
static inline void
|
||||
snl_field_get_ptr(struct snl_state *ss __unused, void *src, void *target)
|
||||
{
|
||||
*((void **)target) = src;
|
||||
}
|
||||
|
||||
struct snl_errmsg_data {
|
||||
uint32_t nlmsg_seq;
|
||||
struct nlmsghdr *orig_hdr;
|
||||
int error;
|
||||
char *error_str;
|
||||
uint32_t error_offs;
|
||||
char *error_str;
|
||||
struct nlattr *cookie;
|
||||
};
|
||||
|
||||
#define _IN(_field) offsetof(struct nlmsgerr, _field)
|
||||
#define _OUT(_field) offsetof(struct snl_errmsg_data, _field)
|
||||
static const struct snl_attr_parser nla_p_errmsg[] = {
|
||||
@ -592,7 +606,7 @@ static const struct snl_attr_parser nla_p_errmsg[] = {
|
||||
|
||||
static const struct snl_field_parser nlf_p_errmsg[] = {
|
||||
{ .off_in = _IN(error), .off_out = _OUT(error), .cb = snl_field_get_uint32 },
|
||||
{ .off_in = _IN(msg.nlmsg_seq), .off_out = _OUT(nlmsg_seq), .cb = snl_field_get_uint32 },
|
||||
{ .off_in = _IN(msg), .off_out = _OUT(orig_hdr), .cb = snl_field_get_ptr },
|
||||
};
|
||||
#undef _IN
|
||||
#undef _OUT
|
||||
@ -609,6 +623,22 @@ static const struct snl_field_parser nlf_p_donemsg[] = {
|
||||
#undef _OUT
|
||||
SNL_DECLARE_PARSER(snl_donemsg_parser, struct nlmsgerr, nlf_p_donemsg, nla_p_donemsg);
|
||||
|
||||
static inline bool
|
||||
snl_parse_errmsg(struct snl_state *ss, struct nlmsghdr *hdr, struct snl_errmsg_data *e)
|
||||
{
|
||||
if ((hdr->nlmsg_flags & NLM_F_CAPPED) != 0)
|
||||
return (snl_parse_nlmsg(ss, hdr, &snl_errmsg_parser, e));
|
||||
|
||||
const struct snl_hdr_parser *ps = &snl_errmsg_parser;
|
||||
struct nlmsgerr *errmsg = (struct nlmsgerr *)(hdr + 1);
|
||||
int hdrlen = sizeof(int) + NLMSG_ALIGN(errmsg->msg.nlmsg_len);
|
||||
struct nlattr *attr_head = (struct nlattr *)(void *)((char *)errmsg + hdrlen);
|
||||
int attr_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
|
||||
|
||||
snl_parse_fields(ss, (struct nlmsghdr *)errmsg, hdrlen, ps->fp, ps->fp_size, e);
|
||||
return (snl_parse_attrs_raw(ss, attr_head, attr_len, ps->np, ps->np_size, e));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
snl_read_reply_code(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_data *e)
|
||||
{
|
||||
@ -617,7 +647,7 @@ snl_read_reply_code(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_
|
||||
if (hdr == NULL) {
|
||||
e->error = EINVAL;
|
||||
} else if (hdr->nlmsg_type == NLMSG_ERROR) {
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_errmsg_parser, e))
|
||||
if (!snl_parse_errmsg(ss, hdr, e))
|
||||
e->error = EINVAL;
|
||||
return (e->error == 0);
|
||||
}
|
||||
@ -636,7 +666,7 @@ snl_read_reply_multi(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg
|
||||
if (hdr == NULL) {
|
||||
e->error = EINVAL;
|
||||
} else if (hdr->nlmsg_type == NLMSG_ERROR) {
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_errmsg_parser, e))
|
||||
if (!snl_parse_errmsg(ss, hdr, e))
|
||||
e->error = EINVAL;
|
||||
} if (hdr->nlmsg_type == NLMSG_DONE) {
|
||||
snl_parse_nlmsg(ss, hdr, &snl_donemsg_parser, e);
|
||||
|
@ -44,6 +44,129 @@ ATF_TC_BODY(snl_verify_route_parsers, tc)
|
||||
|
||||
}
|
||||
|
||||
ATF_TC(snl_parse_errmsg_capped);
|
||||
ATF_TC_HEAD(snl_parse_errmsg_capped, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(snl_parse_errmsg_capped, tc)
|
||||
{
|
||||
struct snl_state ss;
|
||||
struct snl_writer nw;
|
||||
|
||||
require_netlink();
|
||||
|
||||
if (!snl_init(&ss, NETLINK_ROUTE))
|
||||
atf_tc_fail("snl_init() failed");
|
||||
|
||||
int optval = 1;
|
||||
ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_CAP_ACK, &optval, sizeof(optval)) == 0);
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
|
||||
ATF_CHECK(hdr != NULL);
|
||||
ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
|
||||
snl_add_msg_attr_string(&nw, 143, "some random string");
|
||||
ATF_CHECK(snl_finalize_msg(&nw) != NULL);
|
||||
|
||||
ATF_CHECK(snl_send_message(&ss, hdr));
|
||||
|
||||
struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
|
||||
ATF_CHECK(rx_hdr != NULL);
|
||||
ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
ATF_CHECK(rx_hdr->nlmsg_len == sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr));
|
||||
ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
|
||||
ATF_CHECK(e.error != 0);
|
||||
ATF_CHECK(!memcmp(hdr, e.orig_hdr, sizeof(struct nlmsghdr)));
|
||||
}
|
||||
|
||||
ATF_TC(snl_parse_errmsg_capped_extack);
|
||||
ATF_TC_HEAD(snl_parse_errmsg_capped_extack, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors with extack");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(snl_parse_errmsg_capped_extack, tc)
|
||||
{
|
||||
struct snl_state ss;
|
||||
struct snl_writer nw;
|
||||
|
||||
require_netlink();
|
||||
|
||||
if (!snl_init(&ss, NETLINK_ROUTE))
|
||||
atf_tc_fail("snl_init() failed");
|
||||
|
||||
int optval = 1;
|
||||
ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_CAP_ACK, &optval, sizeof(optval)) == 0);
|
||||
optval = 1;
|
||||
ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_EXT_ACK, &optval, sizeof(optval)) == 0);
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
|
||||
ATF_CHECK(hdr != NULL);
|
||||
ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
|
||||
snl_add_msg_attr_string(&nw, 143, "some random string");
|
||||
ATF_CHECK(snl_finalize_msg(&nw) != NULL);
|
||||
|
||||
ATF_CHECK(snl_send_message(&ss, hdr));
|
||||
|
||||
struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
|
||||
ATF_CHECK(rx_hdr != NULL);
|
||||
ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
|
||||
ATF_CHECK(e.error != 0);
|
||||
ATF_CHECK(!memcmp(hdr, e.orig_hdr, sizeof(struct nlmsghdr)));
|
||||
|
||||
ATF_CHECK(e.error_str != NULL);
|
||||
}
|
||||
|
||||
ATF_TC(snl_parse_errmsg_uncapped_extack);
|
||||
ATF_TC_HEAD(snl_parse_errmsg_uncapped_extack, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing errors with extack");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(snl_parse_errmsg_uncapped_extack, tc)
|
||||
{
|
||||
struct snl_state ss;
|
||||
struct snl_writer nw;
|
||||
|
||||
require_netlink();
|
||||
|
||||
ATF_CHECK(snl_init(&ss, NETLINK_ROUTE));
|
||||
|
||||
int optval = 1;
|
||||
ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_EXT_ACK, &optval, sizeof(optval)) == 0);
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
|
||||
ATF_CHECK(hdr != NULL);
|
||||
ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
|
||||
snl_add_msg_attr_string(&nw, 143, "some random string");
|
||||
ATF_CHECK(snl_finalize_msg(&nw) != NULL);
|
||||
|
||||
ATF_CHECK(snl_send_message(&ss, hdr));
|
||||
|
||||
struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
|
||||
ATF_CHECK(rx_hdr != NULL);
|
||||
ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
|
||||
ATF_CHECK(e.error != 0);
|
||||
ATF_CHECK(!memcmp(hdr, e.orig_hdr, hdr->nlmsg_len));
|
||||
|
||||
ATF_CHECK(e.error_str != NULL);
|
||||
}
|
||||
|
||||
ATF_TC(snl_list_ifaces);
|
||||
ATF_TC_HEAD(snl_list_ifaces, tc)
|
||||
{
|
||||
@ -105,6 +228,9 @@ ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, snl_verify_core_parsers);
|
||||
ATF_TP_ADD_TC(tp, snl_verify_route_parsers);
|
||||
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped);
|
||||
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped_extack);
|
||||
ATF_TP_ADD_TC(tp, snl_parse_errmsg_uncapped_extack);
|
||||
ATF_TP_ADD_TC(tp, snl_list_ifaces);
|
||||
|
||||
return (atf_no_error());
|
||||
|
Loading…
Reference in New Issue
Block a user