]> granicus.if.org Git - strace/blob - netlink.c
netlink: implement generic nlmsg_type decoding
[strace] / netlink.c
1 /*
2  * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
3  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
4  * Copyright (c) 2016-2017 The strace developers.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "defs.h"
31 #include <sys/socket.h>
32 #include <linux/netlink.h>
33 #include "xlat/netlink_flags.h"
34 #include "xlat/netlink_protocols.h"
35 #include "xlat/netlink_types.h"
36 #include "xlat/nl_sock_diag_types.h"
37
38 #undef NLMSG_HDRLEN
39 #define NLMSG_HDRLEN NLMSG_ALIGN(sizeof(struct nlmsghdr))
40
41 /*
42  * Fetch a struct nlmsghdr from the given address.
43  */
44 static bool
45 fetch_nlmsghdr(struct tcb *const tcp, struct nlmsghdr *const nlmsghdr,
46                const kernel_ulong_t addr, const kernel_ulong_t len)
47 {
48         if (len < sizeof(struct nlmsghdr)) {
49                 printstrn(tcp, addr, len);
50                 return false;
51         }
52
53         if (umove_or_printaddr(tcp, addr, nlmsghdr))
54                 return false;
55
56         return true;
57 }
58
59 enum {
60         NL_FAMILY_ERROR = -1,
61         NL_FAMILY_DEFAULT = -2
62 };
63
64 static int
65 get_fd_nl_family(struct tcb *const tcp, const int fd)
66 {
67         const unsigned long inode = getfdinode(tcp, fd);
68         if (!inode)
69                 return NL_FAMILY_ERROR;
70
71         const char *const details = get_sockaddr_by_inode(tcp, fd, inode);
72         if (!details)
73                 return NL_FAMILY_ERROR;
74
75         const char *const nl_details = STR_STRIP_PREFIX(details, "NETLINK:[");
76         if (nl_details == details)
77                 return NL_FAMILY_ERROR;
78
79         const struct xlat *xlats = netlink_protocols;
80         for (; xlats->str; ++xlats) {
81                 const char *name = STR_STRIP_PREFIX(xlats->str, "NETLINK_");
82                 if (!strncmp(nl_details, name, strlen(name)))
83                         return xlats->val;
84         }
85
86         if (*nl_details >= '0' && *nl_details <= '9')
87                 return atoi(nl_details);
88
89         return NL_FAMILY_ERROR;
90 }
91
92 static const struct {
93         const struct xlat *const xlat;
94         const char *const dflt;
95 } nlmsg_types[] = {
96         [NETLINK_SOCK_DIAG] = { nl_sock_diag_types, "SOCK_DIAG_???" }
97 };
98
99 /*
100  * As all valid netlink families are positive integers, use unsigned int
101  * for family here to filter out NL_FAMILY_ERROR and NL_FAMILY_DEFAULT.
102  */
103 static void
104 decode_nlmsg_type(const uint16_t type, const unsigned int family)
105 {
106         if (family < ARRAY_SIZE(nlmsg_types)
107             && nlmsg_types[family].xlat) {
108                 printxval(nlmsg_types[family].xlat, type, nlmsg_types[family].dflt);
109         } else {
110                 printxval(netlink_types, type, "NLMSG_???");
111         }
112 }
113
114 static int
115 print_nlmsghdr(struct tcb *tcp,
116                const int fd,
117                int family,
118                const struct nlmsghdr *const nlmsghdr)
119 {
120         /* print the whole structure regardless of its nlmsg_len */
121
122         tprintf("{len=%u, type=", nlmsghdr->nlmsg_len);
123
124         const int hdr_family = (nlmsghdr->nlmsg_type < NLMSG_MIN_TYPE)
125                                ? NL_FAMILY_DEFAULT
126                                : (family != NL_FAMILY_DEFAULT
127                                   ? family : get_fd_nl_family(tcp, fd));
128
129         decode_nlmsg_type(nlmsghdr->nlmsg_type, hdr_family);
130
131         tprints(", flags=");
132         printflags(netlink_flags, nlmsghdr->nlmsg_flags, "NLM_F_???");
133
134         tprintf(", seq=%u, pid=%u}", nlmsghdr->nlmsg_seq,
135                 nlmsghdr->nlmsg_pid);
136
137         return family != NL_FAMILY_DEFAULT ? family : hdr_family;
138 }
139
140 static void
141 decode_nlmsghdr_with_payload(struct tcb *const tcp,
142                              const int fd,
143                              int family,
144                              const struct nlmsghdr *const nlmsghdr,
145                              const kernel_ulong_t addr,
146                              const kernel_ulong_t len);
147
148 static void
149 decode_nlmsgerr(struct tcb *const tcp,
150                 const int fd,
151                 const int family,
152                 kernel_ulong_t addr,
153                 kernel_ulong_t len)
154 {
155         struct nlmsgerr err;
156
157         if (len < sizeof(err.error)) {
158                 printstrn(tcp, addr, len);
159                 return;
160         }
161
162         if (umove_or_printaddr(tcp, addr, &err.error))
163                 return;
164
165         tprints("{error=");
166         if (err.error < 0 && (unsigned) -err.error < nerrnos) {
167                 tprintf("-%s", errnoent[-err.error]);
168         } else {
169                 tprintf("%d", err.error);
170         }
171
172         addr += offsetof(struct nlmsgerr, msg);
173         len -= offsetof(struct nlmsgerr, msg);
174
175         if (len) {
176                 tprints(", msg=");
177                 if (fetch_nlmsghdr(tcp, &err.msg, addr, len)) {
178                         decode_nlmsghdr_with_payload(tcp, fd, family,
179                                                      &err.msg, addr, len);
180                 }
181         }
182
183         tprints("}");
184 }
185
186 static void
187 decode_payload(struct tcb *const tcp,
188                const int fd,
189                const int family,
190                const struct nlmsghdr *const nlmsghdr,
191                const kernel_ulong_t addr,
192                const kernel_ulong_t len)
193 {
194         if (nlmsghdr->nlmsg_type == NLMSG_ERROR) {
195                 decode_nlmsgerr(tcp, fd, family, addr, len);
196                 return;
197         } else if (nlmsghdr->nlmsg_type == NLMSG_DONE && len == sizeof(int)) {
198                 int num;
199
200                 if (!umove_or_printaddr(tcp, addr, &num))
201                         tprintf("%d", num);
202                 return;
203         }
204
205         printstrn(tcp, addr, len);
206 }
207
208 static void
209 decode_nlmsghdr_with_payload(struct tcb *const tcp,
210                              const int fd,
211                              int family,
212                              const struct nlmsghdr *const nlmsghdr,
213                              const kernel_ulong_t addr,
214                              const kernel_ulong_t len)
215 {
216         tprints("{");
217
218         family = print_nlmsghdr(tcp, fd, family, nlmsghdr);
219
220         unsigned int nlmsg_len =
221                 nlmsghdr->nlmsg_len > len ? len : nlmsghdr->nlmsg_len;
222         if (nlmsg_len > NLMSG_HDRLEN) {
223                 tprints(", ");
224                 decode_payload(tcp, fd, family, nlmsghdr, addr + NLMSG_HDRLEN,
225                                                      nlmsg_len - NLMSG_HDRLEN);
226         }
227
228         tprints("}");
229 }
230
231 void
232 decode_netlink(struct tcb *const tcp,
233                const int fd,
234                kernel_ulong_t addr,
235                kernel_ulong_t len)
236 {
237         struct nlmsghdr nlmsghdr;
238         bool print_array = false;
239         unsigned int elt;
240
241         for (elt = 0; fetch_nlmsghdr(tcp, &nlmsghdr, addr, len); elt++) {
242                 if (abbrev(tcp) && elt == max_strlen) {
243                         tprints("...");
244                         break;
245                 }
246
247                 unsigned int nlmsg_len = NLMSG_ALIGN(nlmsghdr.nlmsg_len);
248                 kernel_ulong_t next_addr = 0;
249                 kernel_ulong_t next_len = 0;
250
251                 if (nlmsghdr.nlmsg_len >= NLMSG_HDRLEN) {
252                         next_len = (len >= nlmsg_len) ? len - nlmsg_len : 0;
253
254                         if (next_len && addr + nlmsg_len > addr)
255                                 next_addr = addr + nlmsg_len;
256                 }
257
258                 if (!print_array && next_addr) {
259                         tprints("[");
260                         print_array = true;
261                 }
262
263                 decode_nlmsghdr_with_payload(tcp, fd, NL_FAMILY_DEFAULT,
264                                              &nlmsghdr, addr, len);
265
266                 if (!next_addr)
267                         break;
268
269                 tprints(", ");
270                 addr = next_addr;
271                 len = next_len;
272         }
273
274         if (print_array) {
275                 tprints("]");
276         }
277 }