Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: add error reporting in XDP

Parse netlink ext attribute to get the error message returned by
the card. Code is partially take from libnl.

We add netlink.h to the uapi include of tools. And we need to
avoid include of userspace netlink header to have a successful
build of sample so nlattr.h has a define to avoid
the inclusion. Using a direct define could have been an issue
as NLMSGERR_ATTR_MAX can change in the future.

We also define SOL_NETLINK if not defined to avoid to have to
copy socket.h for a fixed value.

Signed-off-by: Eric Leblond <eric@regit.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Eric Leblond and committed by
Alexei Starovoitov
bbf48c18 949abbe8

+272 -2
+1 -1
samples/bpf/Makefile
··· 45 45 hostprogs-y += syscall_tp 46 46 47 47 # Libbpf dependencies 48 - LIBBPF := ../../tools/lib/bpf/bpf.o 48 + LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o 49 49 CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o 50 50 51 51 test_lru_dist-objs := test_lru_dist.o $(LIBBPF)
+1 -1
tools/lib/bpf/Build
··· 1 - libbpf-y := libbpf.o bpf.o 1 + libbpf-y := libbpf.o bpf.o nlattr.o
+11
tools/lib/bpf/bpf.c
··· 32 32 #include <sys/socket.h> 33 33 #include <errno.h> 34 34 35 + #ifndef SOL_NETLINK 36 + #define SOL_NETLINK 270 37 + #endif 38 + 35 39 /* 36 40 * When building perf, unistd.h is overridden. __NR_bpf is 37 41 * required to be defined explicitly. ··· 440 436 struct nlmsghdr *nh; 441 437 struct nlmsgerr *err; 442 438 socklen_t addrlen; 439 + int one = 1; 443 440 444 441 memset(&sa, 0, sizeof(sa)); 445 442 sa.nl_family = AF_NETLINK; ··· 448 443 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 449 444 if (sock < 0) { 450 445 return -errno; 446 + } 447 + 448 + if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 449 + &one, sizeof(one)) < 0) { 450 + fprintf(stderr, "Netlink error reporting not supported\n"); 451 451 } 452 452 453 453 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { ··· 531 521 if (!err->error) 532 522 continue; 533 523 ret = err->error; 524 + nla_dump_errormsg(nh); 534 525 goto cleanup; 535 526 case NLMSG_DONE: 536 527 break;
+187
tools/lib/bpf/nlattr.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1 2 + 3 + /* 4 + * NETLINK Netlink attributes 5 + * 6 + * This library is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU Lesser General Public 8 + * License as published by the Free Software Foundation version 2.1 9 + * of the License. 10 + * 11 + * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch> 12 + */ 13 + 14 + #include <errno.h> 15 + #include "nlattr.h" 16 + #include <linux/rtnetlink.h> 17 + #include <string.h> 18 + #include <stdio.h> 19 + 20 + static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { 21 + [NLA_U8] = sizeof(uint8_t), 22 + [NLA_U16] = sizeof(uint16_t), 23 + [NLA_U32] = sizeof(uint32_t), 24 + [NLA_U64] = sizeof(uint64_t), 25 + [NLA_STRING] = 1, 26 + [NLA_FLAG] = 0, 27 + }; 28 + 29 + static int nla_len(const struct nlattr *nla) 30 + { 31 + return nla->nla_len - NLA_HDRLEN; 32 + } 33 + 34 + static struct nlattr *nla_next(const struct nlattr *nla, int *remaining) 35 + { 36 + int totlen = NLA_ALIGN(nla->nla_len); 37 + 38 + *remaining -= totlen; 39 + return (struct nlattr *) ((char *) nla + totlen); 40 + } 41 + 42 + static int nla_ok(const struct nlattr *nla, int remaining) 43 + { 44 + return remaining >= sizeof(*nla) && 45 + nla->nla_len >= sizeof(*nla) && 46 + nla->nla_len <= remaining; 47 + } 48 + 49 + static void *nla_data(const struct nlattr *nla) 50 + { 51 + return (char *) nla + NLA_HDRLEN; 52 + } 53 + 54 + static int nla_type(const struct nlattr *nla) 55 + { 56 + return nla->nla_type & NLA_TYPE_MASK; 57 + } 58 + 59 + static int validate_nla(struct nlattr *nla, int maxtype, 60 + struct nla_policy *policy) 61 + { 62 + struct nla_policy *pt; 63 + unsigned int minlen = 0; 64 + int type = nla_type(nla); 65 + 66 + if (type < 0 || type > maxtype) 67 + return 0; 68 + 69 + pt = &policy[type]; 70 + 71 + if (pt->type > NLA_TYPE_MAX) 72 + return 0; 73 + 74 + if (pt->minlen) 75 + minlen = pt->minlen; 76 + else if (pt->type != NLA_UNSPEC) 77 + minlen = nla_attr_minlen[pt->type]; 78 + 79 + if (nla_len(nla) < minlen) 80 + return -1; 81 + 82 + if (pt->maxlen && nla_len(nla) > pt->maxlen) 83 + return -1; 84 + 85 + if (pt->type == NLA_STRING) { 86 + char *data = nla_data(nla); 87 + if (data[nla_len(nla) - 1] != '\0') 88 + return -1; 89 + } 90 + 91 + return 0; 92 + } 93 + 94 + static inline int nlmsg_len(const struct nlmsghdr *nlh) 95 + { 96 + return nlh->nlmsg_len - NLMSG_HDRLEN; 97 + } 98 + 99 + /** 100 + * Create attribute index based on a stream of attributes. 101 + * @arg tb Index array to be filled (maxtype+1 elements). 102 + * @arg maxtype Maximum attribute type expected and accepted. 103 + * @arg head Head of attribute stream. 104 + * @arg len Length of attribute stream. 105 + * @arg policy Attribute validation policy. 106 + * 107 + * Iterates over the stream of attributes and stores a pointer to each 108 + * attribute in the index array using the attribute type as index to 109 + * the array. Attribute with a type greater than the maximum type 110 + * specified will be silently ignored in order to maintain backwards 111 + * compatibility. If \a policy is not NULL, the attribute will be 112 + * validated using the specified policy. 113 + * 114 + * @see nla_validate 115 + * @return 0 on success or a negative error code. 116 + */ 117 + static int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, 118 + struct nla_policy *policy) 119 + { 120 + struct nlattr *nla; 121 + int rem, err; 122 + 123 + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); 124 + 125 + nla_for_each_attr(nla, head, len, rem) { 126 + int type = nla_type(nla); 127 + 128 + if (type > maxtype) 129 + continue; 130 + 131 + if (policy) { 132 + err = validate_nla(nla, maxtype, policy); 133 + if (err < 0) 134 + goto errout; 135 + } 136 + 137 + if (tb[type]) 138 + fprintf(stderr, "Attribute of type %#x found multiple times in message, " 139 + "previous attribute is being ignored.\n", type); 140 + 141 + tb[type] = nla; 142 + } 143 + 144 + err = 0; 145 + errout: 146 + return err; 147 + } 148 + 149 + /* dump netlink extended ack error message */ 150 + int nla_dump_errormsg(struct nlmsghdr *nlh) 151 + { 152 + struct nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = { 153 + [NLMSGERR_ATTR_MSG] = { .type = NLA_STRING }, 154 + [NLMSGERR_ATTR_OFFS] = { .type = NLA_U32 }, 155 + }; 156 + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr; 157 + struct nlmsgerr *err; 158 + char *errmsg = NULL; 159 + int hlen, alen; 160 + 161 + /* no TLVs, nothing to do here */ 162 + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 163 + return 0; 164 + 165 + err = (struct nlmsgerr *)NLMSG_DATA(nlh); 166 + hlen = sizeof(*err); 167 + 168 + /* if NLM_F_CAPPED is set then the inner err msg was capped */ 169 + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) 170 + hlen += nlmsg_len(&err->msg); 171 + 172 + attr = (struct nlattr *) ((void *) err + hlen); 173 + alen = nlh->nlmsg_len - hlen; 174 + 175 + if (nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen, extack_policy) != 0) { 176 + fprintf(stderr, 177 + "Failed to parse extended error attributes\n"); 178 + return 0; 179 + } 180 + 181 + if (tb[NLMSGERR_ATTR_MSG]) 182 + errmsg = (char *) nla_data(tb[NLMSGERR_ATTR_MSG]); 183 + 184 + fprintf(stderr, "Kernel error message: %s\n", errmsg); 185 + 186 + return 0; 187 + }
+72
tools/lib/bpf/nlattr.h
··· 1 + /* SPDX-License-Identifier: LGPL-2.1 */ 2 + 3 + /* 4 + * NETLINK Netlink attributes 5 + * 6 + * This library is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU Lesser General Public 8 + * License as published by the Free Software Foundation version 2.1 9 + * of the License. 10 + * 11 + * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch> 12 + */ 13 + 14 + #ifndef __NLATTR_H 15 + #define __NLATTR_H 16 + 17 + #include <stdint.h> 18 + #include <linux/netlink.h> 19 + /* avoid multiple definition of netlink features */ 20 + #define __LINUX_NETLINK_H 21 + 22 + /** 23 + * Standard attribute types to specify validation policy 24 + */ 25 + enum { 26 + NLA_UNSPEC, /**< Unspecified type, binary data chunk */ 27 + NLA_U8, /**< 8 bit integer */ 28 + NLA_U16, /**< 16 bit integer */ 29 + NLA_U32, /**< 32 bit integer */ 30 + NLA_U64, /**< 64 bit integer */ 31 + NLA_STRING, /**< NUL terminated character string */ 32 + NLA_FLAG, /**< Flag */ 33 + NLA_MSECS, /**< Micro seconds (64bit) */ 34 + NLA_NESTED, /**< Nested attributes */ 35 + __NLA_TYPE_MAX, 36 + }; 37 + 38 + #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) 39 + 40 + /** 41 + * @ingroup attr 42 + * Attribute validation policy. 43 + * 44 + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. 45 + */ 46 + struct nla_policy { 47 + /** Type of attribute or NLA_UNSPEC */ 48 + uint16_t type; 49 + 50 + /** Minimal length of payload required */ 51 + uint16_t minlen; 52 + 53 + /** Maximal length of payload allowed */ 54 + uint16_t maxlen; 55 + }; 56 + 57 + /** 58 + * @ingroup attr 59 + * Iterate over a stream of attributes 60 + * @arg pos loop counter, set to current attribute 61 + * @arg head head of attribute stream 62 + * @arg len length of attribute stream 63 + * @arg rem initialized to len, holds bytes currently remaining in stream 64 + */ 65 + #define nla_for_each_attr(pos, head, len, rem) \ 66 + for (pos = head, rem = len; \ 67 + nla_ok(pos, rem); \ 68 + pos = nla_next(pos, &(rem))) 69 + 70 + int nla_dump_errormsg(struct nlmsghdr *nlh); 71 + 72 + #endif /* __NLATTR_H */