162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <net/netlink.h> 462306a36Sopenharmony_ci#include <linux/drbd_genl_api.h> 562306a36Sopenharmony_ci#include "drbd_nla.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_cistatic int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla) 862306a36Sopenharmony_ci{ 962306a36Sopenharmony_ci struct nlattr *head = nla_data(nla); 1062306a36Sopenharmony_ci int len = nla_len(nla); 1162306a36Sopenharmony_ci int rem; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci /* 1462306a36Sopenharmony_ci * validate_nla (called from nla_parse_nested) ignores attributes 1562306a36Sopenharmony_ci * beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag. 1662306a36Sopenharmony_ci * In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY 1762306a36Sopenharmony_ci * flag set also, check and remove that flag before calling 1862306a36Sopenharmony_ci * nla_parse_nested. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci nla_for_each_attr(nla, head, len, rem) { 2262306a36Sopenharmony_ci if (nla->nla_type & DRBD_GENLA_F_MANDATORY) { 2362306a36Sopenharmony_ci nla->nla_type &= ~DRBD_GENLA_F_MANDATORY; 2462306a36Sopenharmony_ci if (nla_type(nla) > maxtype) 2562306a36Sopenharmony_ci return -EOPNOTSUPP; 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciint drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, 3262306a36Sopenharmony_ci const struct nla_policy *policy) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int err; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci err = drbd_nla_check_mandatory(maxtype, nla); 3762306a36Sopenharmony_ci if (!err) 3862306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, maxtype, nla, policy, 3962306a36Sopenharmony_ci NULL); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return err; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int err; 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * If any nested attribute has the DRBD_GENLA_F_MANDATORY flag set and 4962306a36Sopenharmony_ci * we don't know about that attribute, reject all the nested 5062306a36Sopenharmony_ci * attributes. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci err = drbd_nla_check_mandatory(maxtype, nla); 5362306a36Sopenharmony_ci if (err) 5462306a36Sopenharmony_ci return ERR_PTR(err); 5562306a36Sopenharmony_ci return nla_find_nested(nla, attrtype); 5662306a36Sopenharmony_ci} 57