netlink: add an optional post-process hook to the message parsers.

It is primarily used for adding scopeid to the IPv6 link-local
 sockaddrs. Having proper sockaddrs after parsing minimises the
 possibility of human mistake when using the parsing.

MFC after: 2 weeks
This commit is contained in:
Alexander V. Chernikov 2023-05-15 11:33:10 +00:00
parent 97760572a0
commit 3f6bf6a033
4 changed files with 95 additions and 53 deletions

View file

@ -110,6 +110,7 @@ struct nlattr_parser {
};
typedef bool strict_parser_f(void *hdr, struct nl_pstate *npt);
typedef bool post_parser_f(void *parsed_attrs, struct nl_pstate *npt);
struct nlhdr_parser {
int nl_hdr_off; /* aligned netlink header size */
@ -118,27 +119,26 @@ struct nlhdr_parser {
int np_size;
const struct nlfield_parser *fp; /* array of header field parsers */
const struct nlattr_parser *np; /* array of attribute parsers */
strict_parser_f *sp; /* Parser function */
strict_parser_f *sp; /* Pre-parse strict validation function */
post_parser_f *post_parse;
};
#define NL_DECLARE_PARSER(_name, _t, _fp, _np) \
static const struct nlhdr_parser _name = { \
.nl_hdr_off = sizeof(_t), \
.fp = &((_fp)[0]), \
.np = &((_np)[0]), \
.fp_size = NL_ARRAY_LEN(_fp), \
.np_size = NL_ARRAY_LEN(_np), \
#define NL_DECLARE_PARSER_EXT(_name, _t, _sp, _fp, _np, _pp) \
static const struct nlhdr_parser _name = { \
.nl_hdr_off = sizeof(_t), \
.fp = &((_fp)[0]), \
.np = &((_np)[0]), \
.fp_size = NL_ARRAY_LEN(_fp), \
.np_size = NL_ARRAY_LEN(_np), \
.sp = _sp, \
.post_parse = _pp, \
}
#define NL_DECLARE_STRICT_PARSER(_name, _t, _sp, _fp, _np)\
static const struct nlhdr_parser _name = { \
.nl_hdr_off = sizeof(_t), \
.fp = &((_fp)[0]), \
.np = &((_np)[0]), \
.fp_size = NL_ARRAY_LEN(_fp), \
.np_size = NL_ARRAY_LEN(_np), \
.sp = _sp, \
}
#define NL_DECLARE_PARSER(_name, _t, _fp, _np) \
NL_DECLARE_PARSER_EXT(_name, _t, NULL, _fp, _np, NULL)
#define NL_DECLARE_STRICT_PARSER(_name, _t, _sp, _fp, _np) \
NL_DECLARE_PARSER_EXT(_name, _t, _sp, _fp, _np, NULL)
#define NL_DECLARE_ARR_PARSER(_name, _t, _o, _fp, _np) \
static const struct nlhdr_parser _name = { \
@ -252,6 +252,11 @@ nl_parse_header(void *hdr, int len, const struct nlhdr_parser *parser,
error = nl_parse_attrs_raw(nla_head, len - parser->nl_hdr_off, parser->np,
parser->np_size, npt, target);
if (parser->post_parse != NULL && error == 0) {
if (!parser->post_parse(target, npt))
return (EINVAL);
}
return (error);
}

View file

@ -279,14 +279,6 @@ get_lle(struct netlink_walkargs *wa, struct ifnet *ifp, int family, struct socka
if (llt == NULL)
return (ESRCH);
#ifdef INET6
if (dst->sa_family == AF_INET6) {
struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
if (IN6_IS_SCOPE_LINKLOCAL(&dst6->sin6_addr))
in6_set_unicast_scopeid(&dst6->sin6_addr, ifp->if_index);
}
#endif
struct llentry *lle = lla_lookup(llt, LLE_UNLOCKED, dst);
if (lle == NULL)
return (ESRCH);
@ -297,6 +289,19 @@ get_lle(struct netlink_walkargs *wa, struct ifnet *ifp, int family, struct socka
return (dump_lle(llt, lle, wa));
}
static void
set_scope6(struct sockaddr *sa, struct ifnet *ifp)
{
#ifdef INET6
if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
in6_set_unicast_scopeid(&sa6->sin6_addr, if_getindex(ifp));
}
#endif
}
struct nl_parsed_neigh {
struct sockaddr *nda_dst;
struct ifnet *nda_ifp;
@ -330,7 +335,16 @@ static const struct nlattr_parser nla_p_neigh[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(ndmsg_parser, struct ndmsg, nlf_p_neigh, nla_p_neigh);
static bool
post_p_neigh(void *_attrs, struct nl_pstate *npt __unused)
{
struct nl_parsed_neigh *attrs = (struct nl_parsed_neigh *)_attrs;
set_scope6(attrs->nda_dst, attrs->nda_ifp);
return (true);
}
NL_DECLARE_PARSER_EXT(ndmsg_parser, struct ndmsg, NULL, nlf_p_neigh, nla_p_neigh, post_p_neigh);
/*

View file

@ -678,6 +678,19 @@ nlattr_get_nhg(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void
return (error);
}
static void
set_scope6(struct sockaddr *sa, struct ifnet *ifp)
{
#ifdef INET6
if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
in6_set_unicast_scopeid(&sa6->sin6_addr, if_getindex(ifp));
}
#endif
}
struct nl_parsed_nhop {
uint32_t nha_id;
uint8_t nha_blackhole;
@ -721,7 +734,16 @@ static const struct nlattr_parser nla_p_nh[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(nhmsg_parser, struct nhmsg, nlf_p_nh, nla_p_nh);
static bool
post_p_nh(void *_attrs, struct nl_pstate *npt)
{
struct nl_parsed_nhop *attrs = (struct nl_parsed_nhop *)_attrs;
set_scope6(attrs->nha_gw, attrs->nha_oif);
return (true);
}
NL_DECLARE_PARSER_EXT(nhmsg_parser, struct nhmsg, NULL, nlf_p_nh, nla_p_nh, post_p_nh);
static bool
eligible_nhg(const struct nhop_object *nh)

View file

@ -349,7 +349,6 @@ family_to_group(int family)
return (0);
}
static void
report_operation(uint32_t fibnum, struct rib_cmd_info *rc,
struct nlpcb *nlp, struct nlmsghdr *hdr)
@ -384,6 +383,19 @@ report_operation(uint32_t fibnum, struct rib_cmd_info *rc,
rtsock_callback_p->route_f(fibnum, rc);
}
static void
set_scope6(struct sockaddr *sa, struct ifnet *ifp)
{
#ifdef INET6
if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
in6_set_unicast_scopeid(&sa6->sin6_addr, if_getindex(ifp));
}
#endif
}
struct rta_mpath_nh {
struct sockaddr *gw;
struct ifnet *ifp;
@ -404,26 +416,16 @@ const static struct nlfield_parser nlf_p_rtnh[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(mpath_parser, struct rtnexthop, nlf_p_rtnh, nla_p_rtnh);
static void
set_scope6(struct sockaddr *sa, struct ifnet *ifp)
static bool
post_p_rtnh(void *_attrs, struct nl_pstate *npt __unused)
{
#ifdef INET6
if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
struct rta_mpath_nh *attrs = (struct rta_mpath_nh *)_attrs;
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
in6_set_unicast_scopeid(&sa6->sin6_addr, ifp->if_index);
}
#endif
}
static void
post_p_mpath(struct rta_mpath_nh *mpnh)
{
set_scope6(mpnh->gw, mpnh->ifp);
set_scope6(attrs->gw, attrs->ifp);
return (true);
}
NL_DECLARE_PARSER_EXT(mpath_parser, struct rtnexthop, NULL, nlf_p_rtnh, nla_p_rtnh, post_p_rtnh);
struct rta_mpath {
int num_nhops;
@ -451,7 +453,6 @@ nlattr_get_multipath(struct nlattr *nla, struct nl_pstate *npt, const void *arg,
mp->num_nhops - 1);
return (error);
}
post_p_mpath(mpnh);
int len = NL_ITEM_ALIGN(rtnh->rtnh_len);
data_len -= len;
@ -513,14 +514,17 @@ static const struct nlfield_parser nlf_p_rtmsg[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(rtm_parser, struct rtmsg, nlf_p_rtmsg, nla_p_rtmsg);
static void
post_p_rtmsg(struct nl_parsed_route *r)
static bool
post_p_rtmsg(void *_attrs, struct nl_pstate *npt __unused)
{
set_scope6(r->rta_dst, r->rta_oif);
set_scope6(r->rta_gw, r->rta_oif);
struct nl_parsed_route *attrs = (struct nl_parsed_route *)_attrs;
set_scope6(attrs->rta_dst, attrs->rta_oif);
set_scope6(attrs->rta_gw, attrs->rta_oif);
return (true);
}
NL_DECLARE_PARSER_EXT(rtm_parser, struct rtmsg, NULL, nlf_p_rtmsg, nla_p_rtmsg, post_p_rtmsg);
struct netlink_walkargs {
struct nl_writer *nw;
@ -926,7 +930,6 @@ rtnl_handle_newroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
if (error != 0)
return (error);
post_p_rtmsg(&attrs);
/* Check if we have enough data */
if (attrs.rta_dst == NULL) {
@ -991,7 +994,6 @@ rtnl_handle_delroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
if (error != 0)
return (error);
post_p_rtmsg(&attrs);
if (attrs.rta_dst == NULL) {
NLMSG_REPORT_ERR_MSG(npt, "RTA_DST is not set");
@ -1019,7 +1021,6 @@ rtnl_handle_getroute(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *
error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
if (error != 0)
return (error);
post_p_rtmsg(&attrs);
if (attrs.rta_table >= V_rt_numfibs) {
NLMSG_REPORT_ERR_MSG(npt, "invalid fib");