// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause #ifndef __YNL_C_H #define __YNL_C_H 1 #include #include #include #include struct mnl_socket; struct nlmsghdr; /* * User facing code */ struct ynl_ntf_base_type; struct ynl_ntf_info; struct ynl_sock; enum ynl_error_code { YNL_ERROR_NONE = 0, __YNL_ERRNO_END = 4096, YNL_ERROR_INTERNAL, YNL_ERROR_EXPECT_ACK, YNL_ERROR_EXPECT_MSG, YNL_ERROR_UNEXPECT_MSG, YNL_ERROR_ATTR_MISSING, YNL_ERROR_ATTR_INVALID, YNL_ERROR_UNKNOWN_NTF, YNL_ERROR_INV_RESP, }; /** * struct ynl_error - error encountered by YNL * @code: errno (low values) or YNL error code (enum ynl_error_code) * @attr_offs: offset of bad attribute (for very advanced users) * @msg: error message * * Error information for when YNL operations fail. * Users should interact with the err member of struct ynl_sock directly. * The main exception to that rule is ynl_sock_create(). */ struct ynl_error { enum ynl_error_code code; unsigned int attr_offs; char msg[512]; }; /** * struct ynl_family - YNL family info * Family description generated by codegen. Pass to ynl_sock_create(). */ struct ynl_family { /* private: */ const char *name; const struct ynl_ntf_info *ntf_info; unsigned int ntf_info_size; }; /** * struct ynl_sock - YNL wrapped netlink socket * @err: YNL error descriptor, cleared on every request. */ struct ynl_sock { struct ynl_error err; /* private: */ const struct ynl_family *family; struct mnl_socket *sock; __u32 seq; __u32 portid; __u16 family_id; unsigned int n_mcast_groups; struct { unsigned int id; char name[GENL_NAMSIZ]; } *mcast_groups; struct ynl_ntf_base_type *ntf_first; struct ynl_ntf_base_type **ntf_last_next; struct nlmsghdr *nlh; struct ynl_policy_nest *req_policy; unsigned char *tx_buf; unsigned char *rx_buf; unsigned char raw_buf[]; }; struct ynl_sock * ynl_sock_create(const struct ynl_family *yf, struct ynl_error *e); void ynl_sock_destroy(struct ynl_sock *ys); #define ynl_dump_foreach(dump, iter) \ for (typeof(dump->obj) *iter = &dump->obj; \ !ynl_dump_obj_is_last(iter); \ iter = ynl_dump_obj_next(iter)) int ynl_subscribe(struct ynl_sock *ys, const char *grp_name); int ynl_socket_get_fd(struct ynl_sock *ys); int ynl_ntf_check(struct ynl_sock *ys); /** * ynl_has_ntf() - check if socket has *parsed* notifications * @ys: active YNL socket * * Note that this does not take into account notifications sitting * in netlink socket, just the notifications which have already been * read and parsed (e.g. during a ynl_ntf_check() call). */ static inline bool ynl_has_ntf(struct ynl_sock *ys) { return ys->ntf_last_next != &ys->ntf_first; } struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys); void ynl_ntf_free(struct ynl_ntf_base_type *ntf); /* * YNL internals / low level stuff */ /* Generic mnl helper code */ enum ynl_policy_type { YNL_PT_REJECT = 1, YNL_PT_IGNORE, YNL_PT_NEST, YNL_PT_FLAG, YNL_PT_BINARY, YNL_PT_U8, YNL_PT_U16, YNL_PT_U32, YNL_PT_U64, YNL_PT_UINT, YNL_PT_NUL_STR, YNL_PT_BITFIELD32, }; struct ynl_policy_attr { enum ynl_policy_type type; unsigned int len; const char *name; struct ynl_policy_nest *nest; }; struct ynl_policy_nest { unsigned int max_attr; struct ynl_policy_attr *table; }; struct ynl_parse_arg { struct ynl_sock *ys; struct ynl_policy_nest *rsp_policy; void *data; }; struct ynl_dump_list_type { struct ynl_dump_list_type *next; unsigned char data[] __attribute__((aligned(8))); }; extern struct ynl_dump_list_type *YNL_LIST_END; static inline bool ynl_dump_obj_is_last(void *obj) { unsigned long uptr = (unsigned long)obj; uptr -= offsetof(struct ynl_dump_list_type, data); return uptr == (unsigned long)YNL_LIST_END; } static inline void *ynl_dump_obj_next(void *obj) { unsigned long uptr = (unsigned long)obj; struct ynl_dump_list_type *list; uptr -= offsetof(struct ynl_dump_list_type, data); list = (void *)uptr; uptr = (unsigned long)list->next; uptr += offsetof(struct ynl_dump_list_type, data); return (void *)uptr; } struct ynl_ntf_base_type { __u16 family; __u8 cmd; struct ynl_ntf_base_type *next; void (*free)(struct ynl_ntf_base_type *ntf); unsigned char data[] __attribute__((aligned(8))); }; extern mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE]; struct nlmsghdr * ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version); struct nlmsghdr * ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version); int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr); int ynl_recv_ack(struct ynl_sock *ys, int ret); int ynl_cb_null(const struct nlmsghdr *nlh, void *data); /* YNL specific helpers used by the auto-generated code */ struct ynl_req_state { struct ynl_parse_arg yarg; mnl_cb_t cb; __u32 rsp_cmd; }; struct ynl_dump_state { struct ynl_sock *ys; struct ynl_policy_nest *rsp_policy; void *first; struct ynl_dump_list_type *last; size_t alloc_sz; mnl_cb_t cb; __u32 rsp_cmd; }; struct ynl_ntf_info { struct ynl_policy_nest *policy; mnl_cb_t cb; size_t alloc_sz; void (*free)(struct ynl_ntf_base_type *ntf); }; int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh, struct ynl_req_state *yrs); int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh, struct ynl_dump_state *yds); void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd); int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg); #ifndef MNL_HAS_AUTO_SCALARS static inline uint64_t mnl_attr_get_uint(const struct nlattr *attr) { if (mnl_attr_get_len(attr) == 4) return mnl_attr_get_u32(attr); return mnl_attr_get_u64(attr); } static inline void mnl_attr_put_uint(struct nlmsghdr *nlh, uint16_t type, uint64_t data) { if ((uint32_t)data == (uint64_t)data) return mnl_attr_put_u32(nlh, type, data); return mnl_attr_put_u64(nlh, type, data); } #endif #endif