The branch main has been updated by melifaro:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=568a645ba55a1c3fc4fc74735cb0fab08bfe4cbf

commit 568a645ba55a1c3fc4fc74735cb0fab08bfe4cbf
Author:     Alexander V. Chernikov <[email protected]>
AuthorDate: 2023-03-17 14:27:08 +0000
Commit:     Alexander V. Chernikov <[email protected]>
CommitDate: 2023-03-18 11:35:56 +0000

    netlink: fix capped uncapped ack handling in snl(3).
    
    Reviewed by:    kp
    Differential Revision: https://reviews.freebsd.org/D39144
    MFC after:      2 weeks
---
 sys/netlink/netlink_snl.h    |  52 ++++++++++++++----
 tests/sys/netlink/test_snl.c | 126 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 167 insertions(+), 11 deletions(-)

diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
index be512b67c7ec..935012885362 100644
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -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);
diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c
index 85bdff7fb163..8c6d72f6893b 100644
--- a/tests/sys/netlink/test_snl.c
+++ b/tests/sys/netlink/test_snl.c
@@ -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());

Reply via email to