]>
Commit | Line | Data |
---|---|---|
0ee16e43 MY |
1 | /* |
2 | * lsfd-sock-xinfo.c - read various information from files under /proc/net/ | |
3 | * | |
4 | * Copyright (C) 2022 Red Hat, Inc. All rights reserved. | |
5 | * Written by Masatake YAMATO <yamato@redhat.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software Foundation, | |
19 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | */ | |
2e38e61c MY |
21 | #include <fcntl.h> /* open(2) */ |
22 | #include <linux/net.h> /* SS_* */ | |
23 | #include <linux/un.h> /* UNIX_PATH_MAX */ | |
0ee16e43 MY |
24 | #include <sched.h> /* for setns(2) */ |
25 | #include <search.h> | |
2e38e61c MY |
26 | #include <stdint.h> |
27 | #include <string.h> | |
28 | #include <sys/socket.h> /* SOCK_* */ | |
0ee16e43 MY |
29 | |
30 | #include "xalloc.h" | |
31 | #include "nls.h" | |
32 | #include "libsmartcols.h" | |
33 | ||
34 | #include "lsfd.h" | |
35 | #include "lsfd-sock.h" | |
36 | ||
2e38e61c MY |
37 | static void load_xinfo_from_proc_unix(ino_t netns_inode); |
38 | ||
0ee16e43 MY |
39 | static int self_netns_fd = -1; |
40 | struct stat self_netns_sb; | |
41 | ||
42 | static void *xinfo_tree; /* for tsearch/tfind */ | |
43 | static void *netns_tree; | |
44 | ||
45 | static int netns_compare(const void *a, const void *b) | |
46 | { | |
47 | if (*(ino_t *)a < *(ino_t *)b) | |
48 | return -1; | |
49 | else if (*(ino_t *)a > *(ino_t *)b) | |
50 | return 1; | |
51 | else | |
52 | return 0; | |
53 | } | |
54 | ||
55 | static bool is_sock_xinfo_loaded(ino_t netns) | |
56 | { | |
57 | return tfind(&netns, &netns_tree, netns_compare)? true: false; | |
58 | } | |
59 | ||
60 | static void mark_sock_xinfo_loaded(ino_t ino) | |
61 | { | |
62 | ino_t *netns = xmalloc(sizeof(ino)); | |
63 | ino_t **tmp; | |
64 | ||
65 | *netns = ino; | |
66 | tmp = tsearch(netns, &netns_tree, netns_compare); | |
67 | if (tmp == NULL) | |
68 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
69 | } | |
70 | ||
2e38e61c | 71 | static void load_sock_xinfo_no_nsswitch(ino_t netns) |
0ee16e43 | 72 | { |
2e38e61c | 73 | load_xinfo_from_proc_unix(netns); |
0ee16e43 MY |
74 | } |
75 | ||
76 | static void load_sock_xinfo_with_fd(int fd, ino_t netns) | |
77 | { | |
b3649945 | 78 | if (setns(fd, CLONE_NEWNET) == 0) { |
0ee16e43 | 79 | load_sock_xinfo_no_nsswitch(netns); |
b3649945 | 80 | setns(self_netns_fd, CLONE_NEWNET); |
0ee16e43 MY |
81 | } |
82 | } | |
83 | ||
84 | void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns) | |
85 | { | |
86 | if (self_netns_fd == -1) | |
87 | return; | |
88 | ||
89 | if (!is_sock_xinfo_loaded(netns)) { | |
90 | int fd; | |
91 | ||
92 | mark_sock_xinfo_loaded(netns); | |
93 | fd = ul_path_open(pc, O_RDONLY, name); | |
94 | if (fd < 0) | |
95 | return; | |
96 | ||
97 | load_sock_xinfo_with_fd(fd, netns); | |
98 | close(fd); | |
99 | } | |
100 | } | |
101 | ||
102 | void initialize_sock_xinfos(void) | |
103 | { | |
104 | struct path_cxt *pc; | |
105 | DIR *dir; | |
106 | struct dirent *d; | |
107 | ||
108 | self_netns_fd = open("/proc/self/ns/net", O_RDONLY); | |
109 | ||
110 | if (self_netns_fd < 0) | |
111 | load_sock_xinfo_no_nsswitch(0); | |
112 | else { | |
113 | if (fstat(self_netns_fd, &self_netns_sb) == 0) { | |
114 | mark_sock_xinfo_loaded(self_netns_sb.st_ino); | |
115 | load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino); | |
116 | } | |
117 | } | |
118 | ||
119 | /* Load /proc/net/{unix,...} of the network namespace | |
120 | * specified with netns files under /var/run/netns/. | |
121 | * | |
122 | * `ip netns' command pins a network namespace on | |
123 | * /var/run/netns. | |
124 | */ | |
125 | pc = ul_new_path("/var/run/netns"); | |
126 | if (!pc) | |
127 | err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns")); | |
128 | dir = ul_path_opendir(pc, NULL); | |
129 | if (dir == NULL) { | |
130 | ul_unref_path(pc); | |
131 | return; | |
132 | } | |
133 | while ((d = readdir(dir))) { | |
134 | struct stat sb; | |
135 | int fd; | |
136 | if (ul_path_stat(pc, &sb, 0, d->d_name) < 0) | |
137 | continue; | |
138 | if (is_sock_xinfo_loaded(sb.st_ino)) | |
139 | continue; | |
140 | mark_sock_xinfo_loaded(sb.st_ino); | |
141 | fd = ul_path_open(pc, O_RDONLY, d->d_name); | |
142 | if (fd < 0) | |
143 | continue; | |
144 | load_sock_xinfo_with_fd(fd, sb.st_ino); | |
145 | close(fd); | |
146 | } | |
147 | closedir(dir); | |
148 | ul_unref_path(pc); | |
149 | } | |
150 | ||
b3649945 | 151 | static void free_sock_xinfo(void *node) |
0ee16e43 MY |
152 | { |
153 | struct sock_xinfo *xinfo = node; | |
154 | if (xinfo->class->free) | |
b3649945 | 155 | xinfo->class->free(xinfo); |
0ee16e43 MY |
156 | free(node); |
157 | } | |
158 | ||
159 | void finalize_sock_xinfos(void) | |
160 | { | |
161 | if (self_netns_fd != -1) | |
162 | close(self_netns_fd); | |
163 | tdestroy(netns_tree, free); | |
164 | tdestroy(xinfo_tree, free_sock_xinfo); | |
165 | } | |
166 | ||
167 | static int xinfo_compare(const void *a, const void *b) | |
168 | { | |
169 | if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode) | |
170 | return -1; | |
171 | if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode) | |
172 | return 1; | |
173 | return 0; | |
174 | } | |
175 | ||
2e38e61c MY |
176 | static void add_sock_info(struct sock_xinfo *xinfo) |
177 | { | |
178 | struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare); | |
179 | ||
180 | if (tmp == NULL) | |
181 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
182 | } | |
183 | ||
0ee16e43 MY |
184 | struct sock_xinfo *get_sock_xinfo(ino_t netns_inode) |
185 | { | |
186 | struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare); | |
187 | ||
188 | if (xinfo) | |
189 | return *xinfo; | |
190 | return NULL; | |
191 | } | |
192 | ||
193 | bool is_nsfs_dev(dev_t dev) | |
194 | { | |
195 | return (dev == self_netns_sb.st_dev); | |
196 | } | |
2e38e61c MY |
197 | |
198 | static const char *sock_decode_type(uint16_t type) | |
199 | { | |
200 | switch (type) { | |
201 | case SOCK_STREAM: | |
202 | return "stream"; | |
203 | case SOCK_DGRAM: | |
204 | return "dgram"; | |
205 | case SOCK_RAW: | |
206 | return "raw"; | |
207 | case SOCK_RDM: | |
208 | return "rdm"; | |
209 | case SOCK_SEQPACKET: | |
210 | return "seqpacket"; | |
211 | case SOCK_DCCP: | |
212 | return "dccp"; | |
213 | case SOCK_PACKET: | |
214 | return "packet"; | |
215 | default: | |
216 | return "unknown"; | |
217 | } | |
218 | } | |
219 | ||
220 | /* | |
221 | * Protocol specific code | |
222 | */ | |
223 | ||
224 | /* | |
225 | * UNIX | |
226 | */ | |
227 | struct unix_xinfo { | |
228 | struct sock_xinfo sock; | |
229 | int acceptcon; /* flags */ | |
230 | uint16_t type; | |
231 | uint8_t st; | |
232 | char path[ | |
233 | UNIX_PATH_MAX | |
234 | + 1 /* for @ */ | |
235 | + 1 /* \0? */ | |
236 | ]; | |
237 | }; | |
238 | ||
239 | static const char *unix_decode_state(uint8_t st) | |
240 | { | |
241 | switch (st) { | |
242 | case SS_FREE: | |
243 | return "free"; | |
244 | case SS_UNCONNECTED: | |
245 | return "unconnected"; | |
246 | case SS_CONNECTING: | |
247 | return "connecting"; | |
248 | case SS_CONNECTED: | |
249 | return "connected"; | |
250 | case SS_DISCONNECTING: | |
251 | return "disconnecting"; | |
252 | default: | |
253 | return "unknown"; | |
254 | } | |
255 | } | |
256 | ||
257 | static char *unix_get_name(struct sock_xinfo *sock_xinfo, | |
258 | struct sock *sock) | |
259 | { | |
260 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
261 | const char *state = unix_decode_state(ux->st); | |
262 | char *str = NULL; | |
263 | ||
264 | if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0)) | |
265 | xasprintf(&str, "state=%s%s%s", | |
266 | (ux->acceptcon)? "listen": state, | |
267 | *(ux->path)? " path=": "", | |
268 | *(ux->path)? ux->path: ""); | |
269 | else | |
270 | xasprintf(&str, "state=%s%s%s type=%s", | |
271 | (ux->acceptcon)? "listen": state, | |
272 | *(ux->path)? " path=": "", | |
273 | *(ux->path)? ux->path: "", | |
274 | sock_decode_type(ux->type)); | |
275 | return str; | |
276 | } | |
277 | ||
278 | static char *unix_get_type(struct sock_xinfo *sock_xinfo, | |
279 | struct sock *sock __attribute__((__unused__))) | |
280 | { | |
281 | const char *str; | |
282 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
283 | ||
284 | str = sock_decode_type(ux->type); | |
285 | return strdup(str); | |
286 | } | |
287 | ||
288 | static char *unix_get_state(struct sock_xinfo *sock_xinfo, | |
289 | struct sock *sock __attribute__((__unused__))) | |
290 | { | |
291 | const char *str; | |
292 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
293 | ||
294 | if (ux->acceptcon) | |
295 | return strdup("listen"); | |
296 | ||
297 | str = unix_decode_state(ux->st); | |
298 | return strdup(str); | |
299 | } | |
300 | ||
45d61bff MY |
301 | static bool unix_get_listening(struct sock_xinfo *sock_xinfo, |
302 | struct sock *sock __attribute__((__unused__))) | |
303 | { | |
304 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
305 | ||
306 | return ux->acceptcon; | |
307 | } | |
308 | ||
2e38e61c MY |
309 | static bool unix_fill_column(struct proc *proc __attribute__((__unused__)), |
310 | struct sock_xinfo *sock_xinfo, | |
311 | struct sock *sock __attribute__((__unused__)), | |
312 | struct libscols_line *ln __attribute__((__unused__)), | |
313 | int column_id, | |
314 | size_t column_index __attribute__((__unused__)), | |
315 | char **str) | |
316 | { | |
317 | struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo; | |
318 | ||
319 | switch(column_id) { | |
320 | case COL_UNIX_PATH: | |
321 | if (*ux->path) { | |
322 | *str = strdup(ux->path); | |
323 | return true; | |
324 | } | |
325 | break; | |
326 | } | |
327 | ||
328 | return false; | |
329 | } | |
330 | ||
331 | static struct sock_xinfo_class unix_xinfo_class = { | |
332 | .class = "unix", | |
333 | .get_name = unix_get_name, | |
334 | .get_type = unix_get_type, | |
335 | .get_state = unix_get_state, | |
45d61bff | 336 | .get_listening = unix_get_listening, |
2e38e61c MY |
337 | .fill_column = unix_fill_column, |
338 | .free = NULL, | |
339 | }; | |
340 | ||
341 | /* #define UNIX_LINE_LEN 54 + 21 + UNIX_LINE_LEN + 1 */ | |
342 | #define UNIX_LINE_LEN 256 | |
343 | static void load_xinfo_from_proc_unix(ino_t netns_inode) | |
344 | { | |
345 | char line[UNIX_LINE_LEN]; | |
346 | FILE *unix_fp; | |
347 | ||
348 | unix_fp = fopen("/proc/net/unix", "r"); | |
349 | if (!unix_fp) | |
350 | return; | |
351 | ||
352 | if (fgets(line, sizeof(line), unix_fp) == NULL) | |
353 | goto out; | |
b3649945 | 354 | if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm')) |
2e38e61c MY |
355 | /* Unexpected line */ |
356 | goto out; | |
357 | ||
358 | while (fgets(line, sizeof(line), unix_fp)) { | |
359 | uint64_t flags; | |
360 | uint32_t type; | |
361 | unsigned int st; | |
362 | unsigned long inode; | |
363 | char path[1 + UNIX_PATH_MAX +1]; | |
364 | struct unix_xinfo *ux; | |
365 | ||
366 | memset(path, 0, sizeof(path)); | |
367 | if (sscanf(line, "%*x: %*x %*x %lx %x %x %lu %s", | |
368 | &flags, &type, &st, &inode, path) < 4) | |
369 | continue; | |
370 | ||
371 | if (inode == 0) | |
372 | continue; | |
373 | ||
374 | ux = xmalloc(sizeof(struct unix_xinfo)); | |
375 | ux->sock.class = &unix_xinfo_class; | |
376 | ux->sock.inode = (ino_t)inode; | |
377 | ux->sock.netns_inode = netns_inode; | |
378 | ||
379 | ux->acceptcon = !!flags; | |
380 | ux->type = type; | |
381 | ux->st = st; | |
382 | strcpy(ux->path, path); | |
383 | ||
384 | add_sock_info((struct sock_xinfo *)ux); | |
385 | } | |
386 | ||
387 | out: | |
388 | fclose(unix_fp); | |
389 | } |