aboutsummaryrefslogtreecommitdiffstats
path: root/tools/lib/bpf/nlattr.c
diff options
context:
space:
mode:
authorEric Leblond <eric@regit.org>2018-01-30 15:55:02 -0500
committerAlexei Starovoitov <ast@kernel.org>2018-02-02 20:53:48 -0500
commitbbf48c18ee0cd18b53712aa09aefa29b64b3976e (patch)
treeb8cb67b2922bdd88e43371158a0094fcf09129f3 /tools/lib/bpf/nlattr.c
parent949abbe88436c000cc63fce2bdfeb48b7d06a7df (diff)
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>
Diffstat (limited to 'tools/lib/bpf/nlattr.c')
-rw-r--r--tools/lib/bpf/nlattr.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/tools/lib/bpf/nlattr.c b/tools/lib/bpf/nlattr.c
new file mode 100644
index 000000000000..4719434278b2
--- /dev/null
+++ b/tools/lib/bpf/nlattr.c
@@ -0,0 +1,187 @@
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
20static 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
29static int nla_len(const struct nlattr *nla)
30{
31 return nla->nla_len - NLA_HDRLEN;
32}
33
34static 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
42static 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
49static void *nla_data(const struct nlattr *nla)
50{
51 return (char *) nla + NLA_HDRLEN;
52}
53
54static int nla_type(const struct nlattr *nla)
55{
56 return nla->nla_type & NLA_TYPE_MASK;
57}
58
59static 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
94static 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 */
117static 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;
145errout:
146 return err;
147}
148
149/* dump netlink extended ack error message */
150int 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}