162306a36Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * NETLINK Netlink attributes 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <string.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1362306a36Sopenharmony_ci#include "nlattr.h" 1462306a36Sopenharmony_ci#include "libbpf_internal.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic uint16_t nla_attr_minlen[LIBBPF_NLA_TYPE_MAX+1] = { 1762306a36Sopenharmony_ci [LIBBPF_NLA_U8] = sizeof(uint8_t), 1862306a36Sopenharmony_ci [LIBBPF_NLA_U16] = sizeof(uint16_t), 1962306a36Sopenharmony_ci [LIBBPF_NLA_U32] = sizeof(uint32_t), 2062306a36Sopenharmony_ci [LIBBPF_NLA_U64] = sizeof(uint64_t), 2162306a36Sopenharmony_ci [LIBBPF_NLA_STRING] = 1, 2262306a36Sopenharmony_ci [LIBBPF_NLA_FLAG] = 0, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct nlattr *nla_next(const struct nlattr *nla, int *remaining) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci int totlen = NLA_ALIGN(nla->nla_len); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci *remaining -= totlen; 3062306a36Sopenharmony_ci return (struct nlattr *)((void *)nla + totlen); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int nla_ok(const struct nlattr *nla, int remaining) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return remaining >= (int)sizeof(*nla) && 3662306a36Sopenharmony_ci nla->nla_len >= sizeof(*nla) && 3762306a36Sopenharmony_ci nla->nla_len <= remaining; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int nla_type(const struct nlattr *nla) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return nla->nla_type & NLA_TYPE_MASK; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int validate_nla(struct nlattr *nla, int maxtype, 4662306a36Sopenharmony_ci struct libbpf_nla_policy *policy) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct libbpf_nla_policy *pt; 4962306a36Sopenharmony_ci unsigned int minlen = 0; 5062306a36Sopenharmony_ci int type = nla_type(nla); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (type < 0 || type > maxtype) 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci pt = &policy[type]; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (pt->type > LIBBPF_NLA_TYPE_MAX) 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (pt->minlen) 6162306a36Sopenharmony_ci minlen = pt->minlen; 6262306a36Sopenharmony_ci else if (pt->type != LIBBPF_NLA_UNSPEC) 6362306a36Sopenharmony_ci minlen = nla_attr_minlen[pt->type]; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (libbpf_nla_len(nla) < minlen) 6662306a36Sopenharmony_ci return -1; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen) 6962306a36Sopenharmony_ci return -1; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (pt->type == LIBBPF_NLA_STRING) { 7262306a36Sopenharmony_ci char *data = libbpf_nla_data(nla); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (data[libbpf_nla_len(nla) - 1] != '\0') 7562306a36Sopenharmony_ci return -1; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline int nlmsg_len(const struct nlmsghdr *nlh) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return nlh->nlmsg_len - NLMSG_HDRLEN; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * Create attribute index based on a stream of attributes. 8862306a36Sopenharmony_ci * @arg tb Index array to be filled (maxtype+1 elements). 8962306a36Sopenharmony_ci * @arg maxtype Maximum attribute type expected and accepted. 9062306a36Sopenharmony_ci * @arg head Head of attribute stream. 9162306a36Sopenharmony_ci * @arg len Length of attribute stream. 9262306a36Sopenharmony_ci * @arg policy Attribute validation policy. 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Iterates over the stream of attributes and stores a pointer to each 9562306a36Sopenharmony_ci * attribute in the index array using the attribute type as index to 9662306a36Sopenharmony_ci * the array. Attribute with a type greater than the maximum type 9762306a36Sopenharmony_ci * specified will be silently ignored in order to maintain backwards 9862306a36Sopenharmony_ci * compatibility. If \a policy is not NULL, the attribute will be 9962306a36Sopenharmony_ci * validated using the specified policy. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * @see nla_validate 10262306a36Sopenharmony_ci * @return 0 on success or a negative error code. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciint libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, 10562306a36Sopenharmony_ci int len, struct libbpf_nla_policy *policy) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct nlattr *nla; 10862306a36Sopenharmony_ci int rem, err; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci libbpf_nla_for_each_attr(nla, head, len, rem) { 11362306a36Sopenharmony_ci int type = nla_type(nla); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (type > maxtype) 11662306a36Sopenharmony_ci continue; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (policy) { 11962306a36Sopenharmony_ci err = validate_nla(nla, maxtype, policy); 12062306a36Sopenharmony_ci if (err < 0) 12162306a36Sopenharmony_ci goto errout; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (tb[type]) 12562306a36Sopenharmony_ci pr_warn("Attribute of type %#x found multiple times in message, " 12662306a36Sopenharmony_ci "previous attribute is being ignored.\n", type); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci tb[type] = nla; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci err = 0; 13262306a36Sopenharmony_cierrout: 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * Create attribute index based on nested attribute 13862306a36Sopenharmony_ci * @arg tb Index array to be filled (maxtype+1 elements). 13962306a36Sopenharmony_ci * @arg maxtype Maximum attribute type expected and accepted. 14062306a36Sopenharmony_ci * @arg nla Nested Attribute. 14162306a36Sopenharmony_ci * @arg policy Attribute validation policy. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Feeds the stream of attributes nested into the specified attribute 14462306a36Sopenharmony_ci * to libbpf_nla_parse(). 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * @see libbpf_nla_parse 14762306a36Sopenharmony_ci * @return 0 on success or a negative error code. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ciint libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype, 15062306a36Sopenharmony_ci struct nlattr *nla, 15162306a36Sopenharmony_ci struct libbpf_nla_policy *policy) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci return libbpf_nla_parse(tb, maxtype, libbpf_nla_data(nla), 15462306a36Sopenharmony_ci libbpf_nla_len(nla), policy); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* dump netlink extended ack error message */ 15862306a36Sopenharmony_ciint libbpf_nla_dump_errormsg(struct nlmsghdr *nlh) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct libbpf_nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = { 16162306a36Sopenharmony_ci [NLMSGERR_ATTR_MSG] = { .type = LIBBPF_NLA_STRING }, 16262306a36Sopenharmony_ci [NLMSGERR_ATTR_OFFS] = { .type = LIBBPF_NLA_U32 }, 16362306a36Sopenharmony_ci }; 16462306a36Sopenharmony_ci struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr; 16562306a36Sopenharmony_ci struct nlmsgerr *err; 16662306a36Sopenharmony_ci char *errmsg = NULL; 16762306a36Sopenharmony_ci int hlen, alen; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* no TLVs, nothing to do here */ 17062306a36Sopenharmony_ci if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci err = (struct nlmsgerr *)NLMSG_DATA(nlh); 17462306a36Sopenharmony_ci hlen = sizeof(*err); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* if NLM_F_CAPPED is set then the inner err msg was capped */ 17762306a36Sopenharmony_ci if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) 17862306a36Sopenharmony_ci hlen += nlmsg_len(&err->msg); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci attr = (struct nlattr *) ((void *) err + hlen); 18162306a36Sopenharmony_ci alen = (void *)nlh + nlh->nlmsg_len - (void *)attr; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (libbpf_nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen, 18462306a36Sopenharmony_ci extack_policy) != 0) { 18562306a36Sopenharmony_ci pr_warn("Failed to parse extended error attributes\n"); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (tb[NLMSGERR_ATTR_MSG]) 19062306a36Sopenharmony_ci errmsg = (char *) libbpf_nla_data(tb[NLMSGERR_ATTR_MSG]); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pr_warn("Kernel error message: %s\n", errmsg); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 196