zebra: Fix extended ack error message parsing

Fix the macros for reading NLA attribute info
from an extended error ack. We were processing the data
using route attributes (rtattr) which is identical in size
to nlattr but probably should not be used.

Further, we were incorrectly calculating the length of the
inner netlink message that cause the error. We have to read
passed that in order to access all the nlattr's.

Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
This commit is contained in:
Stephen Worley 2019-03-26 01:03:06 -04:00
parent dfff60ed31
commit 4cebb2b6f6

View File

@ -606,50 +606,56 @@ const char *nl_rttype_to_str(uint8_t rttype)
return lookup_msg(rttype_str, rttype, ""); return lookup_msg(rttype_str, rttype, "");
} }
#define NL_OK(nla, len) \ #define NLA_OK(nla, len) \
((len) >= (int)sizeof(struct nlattr) \ ((len) >= (int)sizeof(struct nlattr) \
&& (nla)->nla_len >= sizeof(struct nlattr) \ && (nla)->nla_len >= sizeof(struct nlattr) \
&& (nla)->nla_len <= (len)) && (nla)->nla_len <= (len))
#define NL_NEXT(nla, attrlen) \ #define NLA_NEXT(nla, attrlen) \
((attrlen) -= RTA_ALIGN((nla)->nla_len), \ ((attrlen) -= NLA_ALIGN((nla)->nla_len), \
(struct nlattr *)(((char *)(nla)) + RTA_ALIGN((nla)->nla_len))) (struct nlattr *)(((char *)(nla)) + NLA_ALIGN((nla)->nla_len)))
#define NL_RTA(r) \ #define NLA_LENGTH(len) (NLA_ALIGN(sizeof(struct nlattr)) + (len))
((struct nlattr *)(((char *)(r)) \ #define NLA_DATA(nla) ((struct nlattr *)(((char *)(nla)) + NLA_LENGTH(0)))
+ NLMSG_ALIGN(sizeof(struct nlmsgerr))))
#define ERR_NLA(err, inner_len) \
((struct nlattr *)(((char *)(err)) \
+ NLMSG_ALIGN(sizeof(struct nlmsgerr)) \
+ NLMSG_ALIGN((inner_len))))
static void netlink_parse_nlattr(struct nlattr **tb, int max, static void netlink_parse_nlattr(struct nlattr **tb, int max,
struct nlattr *nla, int len) struct nlattr *nla, int len)
{ {
while (NL_OK(nla, len)) { while (NLA_OK(nla, len)) {
if (nla->nla_type <= max) if (nla->nla_type <= max)
tb[nla->nla_type] = nla; tb[nla->nla_type] = nla;
nla = NL_NEXT(nla, len); nla = NLA_NEXT(nla, len);
} }
} }
static void netlink_parse_extended_ack(struct nlmsghdr *h) static void netlink_parse_extended_ack(struct nlmsghdr *h)
{ {
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
const struct nlmsgerr *err = const struct nlmsgerr *err = (const struct nlmsgerr *)NLMSG_DATA(h);
(const struct nlmsgerr *)((uint8_t *)h
+ NLMSG_ALIGN(
sizeof(struct nlmsghdr)));
const struct nlmsghdr *err_nlh = NULL; const struct nlmsghdr *err_nlh = NULL;
uint32_t hlen = sizeof(*err); /* Length not including nlmsghdr */
uint32_t len = 0;
/* Inner error netlink message length */
uint32_t inner_len = 0;
const char *msg = NULL; const char *msg = NULL;
uint32_t off = 0; uint32_t off = 0;
if (!(h->nlmsg_flags & NLM_F_CAPPED)) if (!(h->nlmsg_flags & NLM_F_CAPPED))
hlen += h->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); inner_len = (uint32_t)NLMSG_PAYLOAD(&err->msg, 0);
memset(tb, 0, sizeof(tb)); len = (uint32_t)(NLMSG_PAYLOAD(h, sizeof(struct nlmsgerr)) - inner_len);
netlink_parse_nlattr(tb, NLMSGERR_ATTR_MAX, NL_RTA(h), hlen);
netlink_parse_nlattr(tb, NLMSGERR_ATTR_MAX, ERR_NLA(err, inner_len),
len);
if (tb[NLMSGERR_ATTR_MSG]) if (tb[NLMSGERR_ATTR_MSG])
msg = (const char *)RTA_DATA(tb[NLMSGERR_ATTR_MSG]); msg = (const char *)NLA_DATA(tb[NLMSGERR_ATTR_MSG]);
if (tb[NLMSGERR_ATTR_OFFS]) { if (tb[NLMSGERR_ATTR_OFFS]) {
off = *(uint32_t *)RTA_DATA(tb[NLMSGERR_ATTR_OFFS]); off = *(uint32_t *)NLA_DATA(tb[NLMSGERR_ATTR_OFFS]);
if (off > h->nlmsg_len) { if (off > h->nlmsg_len) {
zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS"); zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS");