From a08654ad54d60ba064fb7d51d132d188f0f67330 Mon Sep 17 00:00:00 2001 From: Zibo Gong Date: Fri, 8 Aug 2025 14:41:28 -0700 Subject: [PATCH 1/2] Add TypeBinaryStruct class This patch introduces the `TypeBinaryStruct` class to handle attributes that are of binary type and structured as a `struct` in the `rt-addr`. Unlike the standard `TypeBinary`, `TypeBinaryStruct` has a defined structure, so the `struct_member` should be `optional` and the `presence_type` is `flag`. Signed-off-by: Zibo Gong --- ynl-gen-cpp.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/ynl-gen-cpp.py b/ynl-gen-cpp.py index 4355d77..9d54b57 100755 --- a/ynl-gen-cpp.py +++ b/ynl-gen-cpp.py @@ -513,6 +513,36 @@ def _setter_lines(self, ri, member, presence): ] +class TypeBinaryStruct(TypeBinary): + def struct_member(self, ri): + ri.cw.p(f'std::optional {self.c_name};') + + def presence_type(self): + return "flag" + + def attr_put(self, ri, var): + struct_sz = "sizeof(struct " + c_lower(self.get("struct")) + ")" + self._attr_put_line( + ri, + var, + f"ynl_attr_put(nlh, {self.enum_name}, " + + f"&*{var}.{self.c_name}, {struct_sz})", + ) + + def _attr_get(self, ri, var): + struct_sz = "sizeof(struct " + c_lower(self.get("struct")) + ")" + return ( + [ + "unsigned int len = ynl_attr_data_len(attr);", + f"unsigned int struct_sz = {struct_sz};", + f"{var}->{self.c_name}.emplace();", + f"memcpy(&*{var}->{self.c_name}, ynl_attr_data(attr), std::min(struct_sz, len));", + ], + None, + None, + ) + + class TypeBitfield32(Type): def _complex_member_type(self, ri): return "struct nla_bitfield32" @@ -867,7 +897,10 @@ def new_attr(self, elem, value): elif elem["type"] == "string": t = TypeString(self.family, self, elem, value) elif elem["type"] == "binary": - t = TypeBinary(self.family, self, elem, value) + if "struct" in elem: + t = TypeBinaryStruct(self.family, self, elem, value) + else: + t = TypeBinary(self.family, self, elem, value) elif elem["type"] == "bitfield32": t = TypeBitfield32(self.family, self, elem, value) elif elem["type"] == "nest": From b9265b1ed2279a09ca2f1ac8dc338c904e8e9531 Mon Sep 17 00:00:00 2001 From: Zibo Gong Date: Fri, 8 Aug 2025 14:41:28 -0700 Subject: [PATCH 2/2] ynl-gen-cpp: add support for generating rt-addr This patch add basic support for netlink-raw protocol. The support can correctly generate rt-addr. To test that, a rt-addr test added in samples/. Signed-off-by: Zibo Gong --- generated/Makefile | 2 +- generated/rt-addr-user.cpp | 337 +++++++++++++++++++++++++++++++++++++ generated/rt-addr-user.hpp | 123 ++++++++++++++ lib/ynl-priv.h | 3 + lib/ynl.c | 39 +++-- lib/ynl.h | 2 + samples/.gitignore | 1 + samples/rt-addr.cpp | 54 ++++++ ynl-gen-cpp.py | 117 +++++++------ 9 files changed, 615 insertions(+), 63 deletions(-) create mode 100644 generated/rt-addr-user.cpp create mode 100644 generated/rt-addr-user.hpp create mode 100644 samples/rt-addr.cpp diff --git a/generated/Makefile b/generated/Makefile index 201df91..ea98a49 100644 --- a/generated/Makefile +++ b/generated/Makefile @@ -16,7 +16,7 @@ TOOL:=../ynl-gen-cpp.py GENS_PATHS=$(shell grep -nrI --files-without-match \ 'protocol: netlink' ../Documentation/netlink/specs/) GENS_ALL=$(patsubst ../Documentation/netlink/specs/%.yaml,%,${GENS_PATHS}) -GENS=$(filter-out devlink ovs_datapath ovs_flow ovs_vport nlctrl,${GENS_ALL}) +GENS=rt-addr $(filter-out devlink ovs_datapath ovs_flow ovs_vport nlctrl,${GENS_ALL}) SRCS=$(patsubst %,%-user.cpp,${GENS}) HDRS=$(patsubst %,%-user.hpp,${GENS}) OBJS=$(patsubst %,%-user.cpp.o,${GENS}) diff --git a/generated/rt-addr-user.cpp b/generated/rt-addr-user.cpp new file mode 100644 index 0000000..152e12d --- /dev/null +++ b/generated/rt-addr-user.cpp @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Do not edit directly, auto-generated from: */ +/* */ +/* YNL-GEN user source */ + +#include "rt-addr-user.hpp" + +#include + +#include + +#include + +namespace ynl_cpp { + +/* Enums */ +static constexpr std::array rt_addr_op_strmap = []() { + std::array arr{}; + arr[20] = "getaddr"; + arr[RTM_GETMULTICAST] = "getmulticast"; + return arr; +} (); + +std::string_view rt_addr_op_str(int op) +{ + if (op < 0 || op >= (int)(rt_addr_op_strmap.size())) + return ""; + return rt_addr_op_strmap[op]; +} + +static constexpr std::array rt_addr_ifa_flags_strmap = []() { + std::array arr{}; + arr[0] = "secondary"; + arr[1] = "nodad"; + arr[2] = "optimistic"; + arr[3] = "dadfailed"; + arr[4] = "homeaddress"; + arr[5] = "deprecated"; + arr[6] = "tentative"; + arr[7] = "permanent"; + arr[8] = "managetempaddr"; + arr[9] = "noprefixroute"; + arr[10] = "mcautojoin"; + arr[11] = "stable-privacy"; + return arr; +} (); + +std::string_view rt_addr_ifa_flags_str(int value) +{ + value = (int)(ffs(value) - 1); + if (value < 0 || value >= (int)(rt_addr_ifa_flags_strmap.size())) + return ""; + return rt_addr_ifa_flags_strmap[value]; +} + +/* Policies */ +static std::array rt_addr_addr_attrs_policy = []() { + std::array arr{}; + arr[IFA_ADDRESS].name = "address"; + arr[IFA_ADDRESS].type = YNL_PT_BINARY; + arr[IFA_LOCAL].name = "local"; + arr[IFA_LOCAL].type = YNL_PT_BINARY; + arr[IFA_LABEL].name = "label"; + arr[IFA_LABEL].type = YNL_PT_NUL_STR; + arr[IFA_BROADCAST].name = "broadcast"; + arr[IFA_BROADCAST].type = YNL_PT_BINARY; + arr[IFA_ANYCAST].name = "anycast"; + arr[IFA_ANYCAST].type = YNL_PT_BINARY; + arr[IFA_CACHEINFO].name = "cacheinfo"; + arr[IFA_CACHEINFO].type = YNL_PT_BINARY; + arr[IFA_MULTICAST].name = "multicast"; + arr[IFA_MULTICAST].type = YNL_PT_BINARY; + arr[IFA_FLAGS].name = "flags"; + arr[IFA_FLAGS].type = YNL_PT_U32; + arr[IFA_RT_PRIORITY].name = "rt-priority"; + arr[IFA_RT_PRIORITY].type = YNL_PT_U32; + arr[IFA_TARGET_NETNSID].name = "target-netnsid"; + arr[IFA_TARGET_NETNSID].type = YNL_PT_BINARY; + arr[IFA_PROTO].name = "proto"; + arr[IFA_PROTO].type = YNL_PT_U8; + return arr; +} (); + +struct ynl_policy_nest rt_addr_addr_attrs_nest = { + .max_attr = static_cast(IFA_MAX), + .table = rt_addr_addr_attrs_policy.data(), +}; + +/* Common nested types */ +/* ============== RTM_NEWADDR ============== */ +/* RTM_NEWADDR - do */ +int rt_addr_newaddr(ynl_cpp::ynl_socket& ys, rt_addr_newaddr_req& req) +{ + struct ynl_req_state yrs = { .yarg = { .ys = ys, }, }; + struct nlmsghdr *nlh; + size_t hdr_len; + void *hdr; + int err; + + nlh = ynl_msg_start_req(ys, RTM_NEWADDR, req._nlmsg_flags); + ((struct ynl_sock*)ys)->req_policy = &rt_addr_addr_attrs_nest; + + hdr_len = sizeof(req._hdr); + hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len); + memcpy(hdr, &req._hdr, hdr_len); + + if (req.address.size() > 0) + ynl_attr_put(nlh, IFA_ADDRESS, req.address.data(), req.address.size()); + if (req.label.size() > 0) + ynl_attr_put_str(nlh, IFA_LABEL, req.label.data()); + if (req.local.size() > 0) + ynl_attr_put(nlh, IFA_LOCAL, req.local.data(), req.local.size()); + if (req.cacheinfo) + ynl_attr_put(nlh, IFA_CACHEINFO, &*req.cacheinfo, sizeof(struct ifa_cacheinfo)); + + err = ynl_exec(ys, nlh, &yrs); + if (err < 0) + return -1; + + return 0; +} + +/* ============== RTM_DELADDR ============== */ +/* RTM_DELADDR - do */ +int rt_addr_deladdr(ynl_cpp::ynl_socket& ys, rt_addr_deladdr_req& req) +{ + struct ynl_req_state yrs = { .yarg = { .ys = ys, }, }; + struct nlmsghdr *nlh; + size_t hdr_len; + void *hdr; + int err; + + nlh = ynl_msg_start_req(ys, RTM_DELADDR, req._nlmsg_flags); + ((struct ynl_sock*)ys)->req_policy = &rt_addr_addr_attrs_nest; + + hdr_len = sizeof(req._hdr); + hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len); + memcpy(hdr, &req._hdr, hdr_len); + + if (req.address.size() > 0) + ynl_attr_put(nlh, IFA_ADDRESS, req.address.data(), req.address.size()); + if (req.local.size() > 0) + ynl_attr_put(nlh, IFA_LOCAL, req.local.data(), req.local.size()); + + err = ynl_exec(ys, nlh, &yrs); + if (err < 0) + return -1; + + return 0; +} + +/* ============== RTM_GETADDR ============== */ +/* RTM_GETADDR - dump */ +int rt_addr_getaddr_rsp_dump_parse(const struct nlmsghdr *nlh, + struct ynl_parse_arg *yarg) +{ + rt_addr_getaddr_rsp_dump *dst; + const struct nlattr *attr; + void *hdr; + + dst = (rt_addr_getaddr_rsp_dump*)yarg->data; + + hdr = ynl_nlmsg_data(nlh); + memcpy(&dst->_hdr, hdr, sizeof(struct ifaddrmsg)); + + ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len) { + unsigned int type = ynl_attr_type(attr); + + if (type == IFA_ADDRESS) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + unsigned int len = ynl_attr_data_len(attr); + __u8 *data = (__u8*)ynl_attr_data(attr); + dst->address.assign(data, data + len); + } else if (type == IFA_LABEL) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + dst->label.assign(ynl_attr_get_str(attr)); + } else if (type == IFA_LOCAL) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + unsigned int len = ynl_attr_data_len(attr); + __u8 *data = (__u8*)ynl_attr_data(attr); + dst->local.assign(data, data + len); + } else if (type == IFA_CACHEINFO) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + unsigned int len = ynl_attr_data_len(attr); + unsigned int struct_sz = sizeof(struct ifa_cacheinfo); + dst->cacheinfo.emplace(); + memcpy(&*dst->cacheinfo, ynl_attr_data(attr), std::min(struct_sz, len)); + } + } + + return YNL_PARSE_CB_OK; +} + +std::unique_ptr +rt_addr_getaddr_dump(ynl_cpp::ynl_socket& ys, rt_addr_getaddr_req_dump& req) +{ + struct ynl_dump_no_alloc_state yds = {}; + struct nlmsghdr *nlh; + size_t hdr_len; + void *hdr; + int err; + + auto ret = std::make_unique(); + yds.yarg.ys = ys; + yds.yarg.rsp_policy = &rt_addr_addr_attrs_nest; + yds.yarg.data = ret.get(); + yds.alloc_cb = [](void* arg)->void*{return &(static_cast(arg)->objs.emplace_back());}; + yds.cb = rt_addr_getaddr_rsp_dump_parse; + yds.rsp_cmd = 20; + + nlh = ynl_msg_start_dump(ys, RTM_GETADDR); + hdr_len = sizeof(req._hdr); + hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len); + memcpy(hdr, &req._hdr, hdr_len); + + ((struct ynl_sock*)ys)->req_policy = &rt_addr_addr_attrs_nest; + + err = ynl_exec_dump_no_alloc(ys, nlh, &yds); + if (err < 0) + return nullptr; + + return ret; +} + +/* ============== RTM_GETMULTICAST ============== */ +/* RTM_GETMULTICAST - do */ +int rt_addr_getmulticast_rsp_parse(const struct nlmsghdr *nlh, + struct ynl_parse_arg *yarg) +{ + rt_addr_getmulticast_rsp *dst; + const struct nlattr *attr; + void *hdr; + + dst = (rt_addr_getmulticast_rsp*)yarg->data; + + hdr = ynl_nlmsg_data(nlh); + memcpy(&dst->_hdr, hdr, sizeof(struct ifaddrmsg)); + + ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len) { + unsigned int type = ynl_attr_type(attr); + + if (type == IFA_MULTICAST) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + unsigned int len = ynl_attr_data_len(attr); + __u8 *data = (__u8*)ynl_attr_data(attr); + dst->multicast.assign(data, data + len); + } else if (type == IFA_CACHEINFO) { + if (ynl_attr_validate(yarg, attr)) + return YNL_PARSE_CB_ERROR; + unsigned int len = ynl_attr_data_len(attr); + unsigned int struct_sz = sizeof(struct ifa_cacheinfo); + dst->cacheinfo.emplace(); + memcpy(&*dst->cacheinfo, ynl_attr_data(attr), std::min(struct_sz, len)); + } + } + + return YNL_PARSE_CB_OK; +} + +std::unique_ptr +rt_addr_getmulticast(ynl_cpp::ynl_socket& ys, rt_addr_getmulticast_req& req) +{ + struct ynl_req_state yrs = { .yarg = { .ys = ys, }, }; + std::unique_ptr rsp; + struct nlmsghdr *nlh; + size_t hdr_len; + void *hdr; + int err; + + nlh = ynl_msg_start_req(ys, RTM_GETMULTICAST, req._nlmsg_flags); + ((struct ynl_sock*)ys)->req_policy = &rt_addr_addr_attrs_nest; + yrs.yarg.rsp_policy = &rt_addr_addr_attrs_nest; + + hdr_len = sizeof(req._hdr); + hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len); + memcpy(hdr, &req._hdr, hdr_len); + + rsp.reset(new rt_addr_getmulticast_rsp()); + yrs.yarg.data = rsp.get(); + yrs.cb = rt_addr_getmulticast_rsp_parse; + yrs.rsp_cmd = RTM_GETMULTICAST; + + err = ynl_exec(ys, nlh, &yrs); + if (err < 0) + return nullptr; + + return rsp; +} + +/* RTM_GETMULTICAST - dump */ +std::unique_ptr +rt_addr_getmulticast_dump(ynl_cpp::ynl_socket& ys, + rt_addr_getmulticast_req_dump& req) +{ + struct ynl_dump_no_alloc_state yds = {}; + struct nlmsghdr *nlh; + size_t hdr_len; + void *hdr; + int err; + + auto ret = std::make_unique(); + yds.yarg.ys = ys; + yds.yarg.rsp_policy = &rt_addr_addr_attrs_nest; + yds.yarg.data = ret.get(); + yds.alloc_cb = [](void* arg)->void*{return &(static_cast(arg)->objs.emplace_back());}; + yds.cb = rt_addr_getmulticast_rsp_parse; + yds.rsp_cmd = RTM_GETMULTICAST; + + nlh = ynl_msg_start_dump(ys, RTM_GETMULTICAST); + hdr_len = sizeof(req._hdr); + hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len); + memcpy(hdr, &req._hdr, hdr_len); + + ((struct ynl_sock*)ys)->req_policy = &rt_addr_addr_attrs_nest; + + err = ynl_exec_dump_no_alloc(ys, nlh, &yds); + if (err < 0) + return nullptr; + + return ret; +} + +const struct ynl_family ynl_rt_addr_family = { + .name = "rt_addr", + .is_classic = true, + .classic_id = 0, + .hdr_len = sizeof(struct ifaddrmsg), +}; +const struct ynl_family& get_ynl_rt_addr_family() { + return ynl_rt_addr_family; +}; +} //namespace ynl_cpp diff --git a/generated/rt-addr-user.hpp b/generated/rt-addr-user.hpp new file mode 100644 index 0000000..874a943 --- /dev/null +++ b/generated/rt-addr-user.hpp @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* */ +/* YNL-GEN user header */ + +#ifndef _LINUX_RT_ADDR_GEN_H +#define _LINUX_RT_ADDR_GEN_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ynl.hpp" + +#include + +namespace ynl_cpp { +const struct ynl_family& get_ynl_rt_addr_family(); + +/* Enums */ +std::string_view rt_addr_op_str(int op); +std::string_view rt_addr_ifa_flags_str(int value); + +/* Common nested types */ +/* ============== RTM_NEWADDR ============== */ +/* RTM_NEWADDR - do */ +struct rt_addr_newaddr_req { + __u16 _nlmsg_flags; + + struct ifaddrmsg _hdr; + + std::vector<__u8> address; + std::string label; + std::vector<__u8> local; + std::optional cacheinfo; +}; + +/* + * Add new address + */ +int rt_addr_newaddr(ynl_cpp::ynl_socket& ys, rt_addr_newaddr_req& req); + +/* ============== RTM_DELADDR ============== */ +/* RTM_DELADDR - do */ +struct rt_addr_deladdr_req { + __u16 _nlmsg_flags; + + struct ifaddrmsg _hdr; + + std::vector<__u8> address; + std::vector<__u8> local; +}; + +/* + * Remove address + */ +int rt_addr_deladdr(ynl_cpp::ynl_socket& ys, rt_addr_deladdr_req& req); + +/* ============== RTM_GETADDR ============== */ +/* RTM_GETADDR - dump */ +struct rt_addr_getaddr_req_dump { + struct ifaddrmsg _hdr; +}; + +struct rt_addr_getaddr_rsp_dump { + struct ifaddrmsg _hdr; + + std::vector<__u8> address; + std::string label; + std::vector<__u8> local; + std::optional cacheinfo; +}; + +struct rt_addr_getaddr_rsp_list { + std::list objs; +}; + +std::unique_ptr +rt_addr_getaddr_dump(ynl_cpp::ynl_socket& ys, rt_addr_getaddr_req_dump& req); + +/* ============== RTM_GETMULTICAST ============== */ +/* RTM_GETMULTICAST - do */ +struct rt_addr_getmulticast_req { + __u16 _nlmsg_flags; + + struct ifaddrmsg _hdr; +}; + +struct rt_addr_getmulticast_rsp { + struct ifaddrmsg _hdr; + + std::vector<__u8> multicast; + std::optional cacheinfo; +}; + +/* + * Get / dump IPv4/IPv6 multicast addresses. + */ +std::unique_ptr +rt_addr_getmulticast(ynl_cpp::ynl_socket& ys, rt_addr_getmulticast_req& req); + +/* RTM_GETMULTICAST - dump */ +struct rt_addr_getmulticast_req_dump { + struct ifaddrmsg _hdr; +}; + +struct rt_addr_getmulticast_list { + std::list objs; +}; + +std::unique_ptr +rt_addr_getmulticast_dump(ynl_cpp::ynl_socket& ys, + rt_addr_getmulticast_req_dump& req); + +} //namespace ynl_cpp +#endif /* _LINUX_RT_ADDR_GEN_H */ diff --git a/lib/ynl-priv.h b/lib/ynl-priv.h index 73571eb..9e4dd45 100644 --- a/lib/ynl-priv.h +++ b/lib/ynl-priv.h @@ -94,6 +94,9 @@ struct ynl_ntf_base_type { unsigned char data[] __attribute__((aligned(8))); }; +struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id, __u16 flags); +struct nlmsghdr *ynl_msg_start_dump(struct ynl_sock *ys, __u32 id); + struct nlmsghdr* ynl_gemsg_start_req(struct ynl_sock* ys, __u32 id, __u8 cmd, __u8 version); struct nlmsghdr* diff --git a/lib/ynl.c b/lib/ynl.c index cb1adb9..f2ce46e 100644 --- a/lib/ynl.c +++ b/lib/ynl.c @@ -522,12 +522,12 @@ struct nlmsghdr* ynl_gemsg_start( return nlh; } -void ynl_msg_start_req(struct ynl_sock* ys, __u32 id) { - ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK); +struct nlmsghdr *ynl_msg_start_req(struct ynl_sock* ys, __u32 id, __u16 flags) { + return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | flags); } -void ynl_msg_start_dump(struct ynl_sock* ys, __u32 id) { - ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); +struct nlmsghdr *ynl_msg_start_dump(struct ynl_sock* ys, __u32 id) { + return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); } struct nlmsghdr* @@ -732,6 +732,7 @@ struct ynl_sock* ynl_sock_create( struct ynl_error* yse) { struct sockaddr_nl addr; struct ynl_sock* ys; + int sock_type; socklen_t addrlen; int one = 1; @@ -745,7 +746,9 @@ struct ynl_sock* ynl_sock_create( ys->rx_buf = &ys->raw_buf[YNL_SOCKET_BUFFER_SIZE]; ys->ntf_last_next = &ys->ntf_first; - ys->socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + sock_type = yf->is_classic ? yf->classic_id : NETLINK_GENERIC; + + ys->socket = socket(AF_NETLINK, SOCK_RAW, sock_type); if (ys->socket < 0) { __perr(yse, "failed to create a netlink socket"); goto err_free_sock; @@ -778,7 +781,9 @@ struct ynl_sock* ynl_sock_create( ys->portid = addr.nl_pid; ys->seq = random(); - if (ynl_sock_read_family(ys, yf->name)) { + if (yf->is_classic) { + ys->family_id = yf->classic_id; + } else if (ynl_sock_read_family(ys, yf->name)) { if (yse) memcpy(yse, &ys->err, sizeof(*yse)); goto err_close_sock; @@ -930,17 +935,21 @@ static int ynl_check_alien( struct ynl_sock* ys, const struct nlmsghdr* nlh, __u32 rsp_cmd) { - struct genlmsghdr* gehdr; + if (ys->family->is_classic) { + if (nlh->nlmsg_type != rsp_cmd) + return ynl_ntf_parse(ys, nlh); + } else { + struct genlmsghdr* gehdr; + + if (ynl_nlmsg_data_len(nlh) < sizeof(*gehdr)) { + yerr(ys, YNL_ERROR_INV_RESP, "Kernel responded with truncated message"); + return -1; + } - if (ynl_nlmsg_data_len(nlh) < sizeof(*gehdr)) { - yerr(ys, YNL_ERROR_INV_RESP, "Kernel responded with truncated message"); - return -1; + gehdr = ynl_nlmsg_data(nlh); + if (gehdr->cmd != rsp_cmd) + return ynl_ntf_parse(ys, nlh); } - - gehdr = ynl_nlmsg_data(nlh); - if (gehdr->cmd != rsp_cmd) - return ynl_ntf_parse(ys, nlh); - return 0; } diff --git a/lib/ynl.h b/lib/ynl.h index 601350e..0666724 100644 --- a/lib/ynl.h +++ b/lib/ynl.h @@ -47,6 +47,8 @@ struct ynl_error { struct ynl_family { /* private: */ const char* name; + bool is_classic; + __u16 classic_id; size_t hdr_len; const struct ynl_ntf_info* ntf_info; unsigned int ntf_info_size; diff --git a/samples/.gitignore b/samples/.gitignore index b4f8b18..f94ff51 100644 --- a/samples/.gitignore +++ b/samples/.gitignore @@ -1,2 +1,3 @@ ethtool netdev +rt-addr diff --git a/samples/rt-addr.cpp b/samples/rt-addr.cpp new file mode 100644 index 0000000..95ac453 --- /dev/null +++ b/samples/rt-addr.cpp @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +std::ostream &operator<<(std::ostream &os, + const ynl_cpp::rt_addr_getaddr_rsp_dump &rsp) { + char buf[IF_NAMESIZE]; + char addr_str[64]; + + auto name = if_indextoname(rsp._hdr.ifa_index, buf); + if (name) + os << std::setw(16) << name << ": "; + + if (rsp.address.size() == 4) { + os << inet_ntop(AF_INET, rsp.address.data(), addr_str, sizeof(addr_str)); + } else if (rsp.address.size() == 16) { + os << inet_ntop(AF_INET6, rsp.address.data(), addr_str, sizeof(addr_str)); + } else { + os << "[" << rsp.address.size() << "]"; + } + + return os; +} + +int main(int argc, char **argv) { + ynl_error yerr; + ynl_cpp::ynl_socket ys(ynl_cpp::get_ynl_rt_addr_family(), &yerr); + if (!ys) { + std::cerr << yerr.msg << std::endl; + return -1; + } + + ynl_cpp::rt_addr_getaddr_req_dump req = {}; + std::unique_ptr rsp = + ynl_cpp::rt_addr_getaddr_dump(ys, req); + if (rsp == nullptr) { + std::cerr << "Error: rt-addr getaddr dump fails" << std::endl; + return 2; + } + if (rsp->objs.empty()) { + std::cerr << "Error: no addresses reported" << std::endl; + return 2; + } + + for (const auto &addr : rsp->objs) + std::cout << addr << std::endl; + + return 0; +} diff --git a/ynl-gen-cpp.py b/ynl-gen-cpp.py index 9d54b57..c3107b5 100755 --- a/ynl-gen-cpp.py +++ b/ynl-gen-cpp.py @@ -79,7 +79,7 @@ def __init__(self, family, attr_set, attr, value): if self.c_name in _C_KW: self.c_name += "_" if self.c_name[0].isdigit(): - self.c_name = '_' + self.c_name + self.c_name = "_" + self.c_name # Added by resolve(): self.enum_name = None @@ -286,33 +286,34 @@ def __init__(self, family, attr_set, attr, value): if "byte-order" in attr: self.byte_order_comment = f" /* {attr['byte-order']} */" - if "enum" in self.attr: - enum = self.family.consts[self.attr["enum"]] - low, high = enum.value_range() - if low == None and high == None: - self.checks['sparse'] = True - else: - if "min" not in self.checks: - if low != 0 or self.type[0] == "s": - self.checks["min"] = low - if "max" not in self.checks: - self.checks["max"] = high - - if "min" in self.checks and "max" in self.checks: - if self.get_limit("min") > self.get_limit("max"): + if not family.is_classic(): + if "enum" in self.attr: + enum = self.family.consts[self.attr["enum"]] + low, high = enum.value_range() + if low == None and high == None: + self.checks["sparse"] = True + else: + if "min" not in self.checks: + if low != 0 or self.type[0] == "s": + self.checks["min"] = low + if "max" not in self.checks: + self.checks["max"] = high + + if "min" in self.checks and "max" in self.checks: + if self.get_limit("min") > self.get_limit("max"): + raise Exception( + f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}' + ) + self.checks["range"] = True + + low = min(self.get_limit("min", 0), self.get_limit("max", 0)) + high = max(self.get_limit("min", 0), self.get_limit("max", 0)) + if low < 0 and self.type[0] == "u": raise Exception( - f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}' + f'Invalid limit for "{self.name}" negative limit for unsigned type' ) - self.checks["range"] = True - - low = min(self.get_limit("min", 0), self.get_limit("max", 0)) - high = max(self.get_limit("min", 0), self.get_limit("max", 0)) - if low < 0 and self.type[0] == "u": - raise Exception( - f'Invalid limit for "{self.name}" negative limit for unsigned type' - ) - if low < -32768 or high > 32767: - self.checks["full-range"] = True + if low < -32768 or high > 32767: + self.checks["full-range"] = True # Added by resolve(): self.is_bitfield = None @@ -989,13 +990,6 @@ def __init__(self, file_name, exclude_ops): def resolve(self): self.resolve_up(super()) - if self.yaml.get("protocol", "genetlink") not in { - "genetlink", - "genetlink-c", - "genetlink-legacy", - }: - raise Exception("Codegen only supported for genetlink") - self.c_name = c_lower(self.name) if "name-prefix" in self.yaml["operations"]: self.op_prefix = c_upper(self.yaml["operations"]["name-prefix"]) @@ -1042,6 +1036,9 @@ def new_attr_set(self, elem): def new_operation(self, elem, req_value, rsp_value): return Operation(self, elem, req_value, rsp_value) + def is_classic(self): + return self.proto == "netlink-raw" + def _mark_notify(self): for op in self.msgs.values(): if "notify" in op: @@ -1233,7 +1230,8 @@ def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None): self.op_mode = op_mode self.op = op - self.fixed_hdr = None + self.fixed_hdr = op.fixed_header if op else None + self.fixed_hdr_len = "ys->family->hdr_len" if op and op.fixed_header: self.fixed_hdr = "struct " + c_lower(op.fixed_header) @@ -1276,6 +1274,11 @@ def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None): family, self.attr_set, type_list=op["event"]["attributes"] ) + def needs_nlflags(self, direction): + return ( + self.op_mode == "do" and direction == "request" and self.family.is_classic() + ) + class CodeWriter: def __init__(self, nlib, out_file=None, overwrite=True): @@ -1767,7 +1770,12 @@ def _multi_parse(ri, struct, init_lines, local_vars): ri.cw.p(f"dst->{arg} = {arg};") if ri.fixed_hdr: - ri.cw.p("hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));") + if struct.nested: + ri.cw.p("hdr = ynl_attr_data(nested);") + elif ri.family.is_classic(): + ri.cw.p("hdr = ynl_nlmsg_data(nlh);") + else: + ri.cw.p("hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));") ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({ri.fixed_hdr}));") for anest in sorted(all_multi): aspec = struct[anest] @@ -1917,9 +1925,12 @@ def print_req(ri): ri.cw.block_start() ri.cw.write_func_lvar(local_vars) - ri.cw.p( - f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);" - ) + if ri.family.is_classic(): + ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name}, req._nlmsg_flags);") + else: + ri.cw.p( + f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);" + ) ri.cw.p( f"((struct ynl_sock*)ys)->req_policy = &{ri.struct['request'].render_name}_nest;" @@ -1929,9 +1940,9 @@ def print_req(ri): ri.cw.nl() if ri.fixed_hdr: - ri.cw.p("hdr_len = sizeof(req->_hdr);") + ri.cw.p("hdr_len = sizeof(req._hdr);") ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);") - ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);") + ri.cw.p("memcpy(hdr, &req._hdr, hdr_len);") ri.cw.nl() for _, attr in ri.struct["request"].member_list(): @@ -1991,14 +2002,17 @@ def print_dump(ri): else: ri.cw.p(f"yds.rsp_cmd = {ri.op.rsp_value};") ri.cw.nl() - ri.cw.p( - f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);" - ) + if ri.family.is_classic(): + ri.cw.p(f"nlh = ynl_msg_start_dump(ys, {ri.op.enum_name});") + else: + ri.cw.p( + f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);" + ) if ri.fixed_hdr: - ri.cw.p("hdr_len = sizeof(req->_hdr);") + ri.cw.p("hdr_len = sizeof(req._hdr);") ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);") - ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);") + ri.cw.p("memcpy(hdr, &req._hdr, hdr_len);") ri.cw.nl() if "request" in ri.op[ri.op_mode]: @@ -2030,6 +2044,9 @@ def _print_type(ri, direction, struct): ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}") + if ri.needs_nlflags(direction): + ri.cw.p("__u16 _nlmsg_flags;") + ri.cw.nl() if ri.fixed_hdr: ri.cw.p(ri.fixed_hdr + " _hdr;") ri.cw.nl() @@ -2065,7 +2082,7 @@ def print_parse_prototype(ri, direction, terminate=True): def print_req_type(ri): - if len(ri.struct["request"].attr_list) == 0: + if len(ri.struct["request"].attr_list) == 0 and ri.op.fixed_header is None: return print_type(ri, "request") @@ -2175,7 +2192,13 @@ def render_user_family(family, cw, prototype): cw.block_start(f"{symbol} = ") cw.p(f'.name\t\t= "{family.c_name}",') - if family.fixed_header: + if family.is_classic(): + cw.p(f".is_classic\t= true,") + cw.p(f'.classic_id\t= {family.get("protonum")},') + if family.is_classic(): + if family.fixed_header: + cw.p(f".hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),") + elif family.fixed_header: cw.p( f".hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)})," )