blob: 32f9e397a6c07a988edd80a3338fd2a29930d3e0 [file] [log] [blame]
Jakub Kicinski71bb4282017-10-04 20:10:04 -07001/*
Jakub Kicinskie64d5252018-05-03 18:37:15 -07002 * Copyright (C) 2017-2018 Netronome Systems, Inc.
Jakub Kicinski71bb4282017-10-04 20:10:04 -07003 *
4 * This software is dual licensed under the GNU General License Version 2,
5 * June 1991 as shown in the file COPYING in the top-level directory of this
6 * source tree or the BSD 2-Clause License provided below. You have the
7 * option to license this software under the complete terms of either license.
8 *
9 * The BSD 2-Clause License:
10 *
11 * Redistribution and use in source and binary forms, with or
12 * without modification, are permitted provided that the following
13 * conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials
22 * provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34/* Author: Jakub Kicinski <kubakici@wp.pl> */
35
Jakub Kicinskie64d5252018-05-03 18:37:15 -070036#include <ctype.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070037#include <errno.h>
Jiong Wange6593592018-01-16 16:05:21 -080038#include <fcntl.h>
Prashant Bhole4990f1f2017-11-08 13:55:48 +090039#include <fts.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070040#include <libgen.h>
Prashant Bhole4990f1f2017-11-08 13:55:48 +090041#include <mntent.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070042#include <stdbool.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <linux/limits.h>
48#include <linux/magic.h>
Jakub Kicinski52262212017-12-27 18:39:10 -080049#include <net/if.h>
Quentin Monnet3fc27b72017-10-24 20:11:28 -070050#include <sys/mount.h>
Jakub Kicinski52262212017-12-27 18:39:10 -080051#include <sys/stat.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070052#include <sys/types.h>
53#include <sys/vfs.h>
54
55#include <bpf.h>
56
57#include "main.h"
58
Jiri Benc20d5de52018-03-06 14:50:10 +010059#ifndef BPF_FS_MAGIC
60#define BPF_FS_MAGIC 0xcafe4a11
61#endif
62
Quentin Monnet0b1c27db2017-11-03 13:59:07 -070063void p_err(const char *fmt, ...)
64{
65 va_list ap;
66
67 va_start(ap, fmt);
68 if (json_output) {
69 jsonw_start_object(json_wtr);
70 jsonw_name(json_wtr, "error");
71 jsonw_vprintf_enquote(json_wtr, fmt, ap);
72 jsonw_end_object(json_wtr);
73 } else {
74 fprintf(stderr, "Error: ");
75 vfprintf(stderr, fmt, ap);
76 fprintf(stderr, "\n");
77 }
78 va_end(ap);
79}
80
81void p_info(const char *fmt, ...)
82{
83 va_list ap;
84
85 if (json_output)
86 return;
87
88 va_start(ap, fmt);
89 vfprintf(stderr, fmt, ap);
90 fprintf(stderr, "\n");
91 va_end(ap);
92}
93
Jakub Kicinski71bb4282017-10-04 20:10:04 -070094static bool is_bpffs(char *path)
95{
96 struct statfs st_fs;
97
98 if (statfs(path, &st_fs) < 0)
99 return false;
100
101 return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
102}
103
Quentin Monnet3fc27b72017-10-24 20:11:28 -0700104static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
105{
106 bool bind_done = false;
107
108 while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
109 if (errno != EINVAL || bind_done) {
110 snprintf(buff, bufflen,
111 "mount --make-private %s failed: %s",
112 target, strerror(errno));
113 return -1;
114 }
115
116 if (mount(target, target, "none", MS_BIND, NULL)) {
117 snprintf(buff, bufflen,
118 "mount --bind %s %s failed: %s",
119 target, target, strerror(errno));
120 return -1;
121 }
122
123 bind_done = true;
124 }
125
126 if (mount("bpf", target, "bpf", 0, "mode=0700")) {
127 snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s",
128 target, strerror(errno));
129 return -1;
130 }
131
132 return 0;
133}
134
Prashant Bhole18527192017-11-08 13:55:47 +0900135int open_obj_pinned(char *path)
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700136{
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700137 int fd;
138
139 fd = bpf_obj_get(path);
140 if (fd < 0) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700141 p_err("bpf obj get (%s): %s", path,
142 errno == EACCES && !is_bpffs(dirname(path)) ?
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700143 "directory not in bpf file system (bpffs)" :
144 strerror(errno));
145 return -1;
146 }
147
Prashant Bhole18527192017-11-08 13:55:47 +0900148 return fd;
149}
150
151int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
152{
153 enum bpf_obj_type type;
154 int fd;
155
156 fd = open_obj_pinned(path);
157 if (fd < 0)
158 return -1;
159
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700160 type = get_fd_type(fd);
161 if (type < 0) {
162 close(fd);
163 return type;
164 }
165 if (type != exp_type) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700166 p_err("incorrect object type: %s", get_fd_type_name(type));
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700167 close(fd);
168 return -1;
169 }
170
171 return fd;
172}
173
Roman Gushchin49a086c2017-12-13 15:18:53 +0000174int do_pin_fd(int fd, const char *name)
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700175{
Quentin Monnet3fc27b72017-10-24 20:11:28 -0700176 char err_str[ERR_MAX_LEN];
Quentin Monnet3fc27b72017-10-24 20:11:28 -0700177 char *file;
178 char *dir;
Roman Gushchin49a086c2017-12-13 15:18:53 +0000179 int err = 0;
180
181 err = bpf_obj_pin(fd, name);
182 if (!err)
183 goto out;
184
185 file = malloc(strlen(name) + 1);
186 strcpy(file, name);
187 dir = dirname(file);
188
189 if (errno != EPERM || is_bpffs(dir)) {
190 p_err("can't pin the object (%s): %s", name, strerror(errno));
191 goto out_free;
192 }
193
194 /* Attempt to mount bpffs, then retry pinning. */
195 err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
196 if (!err) {
197 err = bpf_obj_pin(fd, name);
198 if (err)
199 p_err("can't pin the object (%s): %s", name,
200 strerror(errno));
201 } else {
202 err_str[ERR_MAX_LEN - 1] = '\0';
203 p_err("can't mount BPF file system to pin the object (%s): %s",
204 name, err_str);
205 }
206
207out_free:
208 free(file);
209out:
210 return err;
211}
212
213int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
214{
215 unsigned int id;
216 char *endptr;
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700217 int err;
218 int fd;
219
220 if (!is_prefix(*argv, "id")) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700221 p_err("expected 'id' got %s", *argv);
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700222 return -1;
223 }
224 NEXT_ARG();
225
226 id = strtoul(*argv, &endptr, 0);
227 if (*endptr) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700228 p_err("can't parse %s as ID", *argv);
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700229 return -1;
230 }
231 NEXT_ARG();
232
233 if (argc != 1)
234 usage();
235
236 fd = get_fd_by_id(id);
237 if (fd < 0) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700238 p_err("can't get prog by id (%u): %s", id, strerror(errno));
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700239 return -1;
240 }
241
Roman Gushchin49a086c2017-12-13 15:18:53 +0000242 err = do_pin_fd(fd, *argv);
Quentin Monnet3fc27b72017-10-24 20:11:28 -0700243
Quentin Monnet3fc27b72017-10-24 20:11:28 -0700244 close(fd);
245 return err;
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700246}
247
248const char *get_fd_type_name(enum bpf_obj_type type)
249{
250 static const char * const names[] = {
251 [BPF_OBJ_UNKNOWN] = "unknown",
252 [BPF_OBJ_PROG] = "prog",
253 [BPF_OBJ_MAP] = "map",
254 };
255
256 if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
257 return names[BPF_OBJ_UNKNOWN];
258
259 return names[type];
260}
261
262int get_fd_type(int fd)
263{
264 char path[PATH_MAX];
265 char buf[512];
266 ssize_t n;
267
268 snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
269
270 n = readlink(path, buf, sizeof(buf));
271 if (n < 0) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700272 p_err("can't read link type: %s", strerror(errno));
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700273 return -1;
274 }
275 if (n == sizeof(path)) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700276 p_err("can't read link type: path too long!");
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700277 return -1;
278 }
279
280 if (strstr(buf, "bpf-map"))
281 return BPF_OBJ_MAP;
282 else if (strstr(buf, "bpf-prog"))
283 return BPF_OBJ_PROG;
284
285 return BPF_OBJ_UNKNOWN;
286}
287
288char *get_fdinfo(int fd, const char *key)
289{
290 char path[PATH_MAX];
291 char *line = NULL;
292 size_t line_n = 0;
293 ssize_t n;
294 FILE *fdi;
295
296 snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
297
298 fdi = fopen(path, "r");
299 if (!fdi) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700300 p_err("can't open fdinfo: %s", strerror(errno));
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700301 return NULL;
302 }
303
304 while ((n = getline(&line, &line_n, fdi))) {
305 char *value;
306 int len;
307
308 if (!strstr(line, key))
309 continue;
310
311 fclose(fdi);
312
313 value = strchr(line, '\t');
314 if (!value || !value[1]) {
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700315 p_err("malformed fdinfo!?");
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700316 free(line);
317 return NULL;
318 }
319 value++;
320
321 len = strlen(value);
322 memmove(line, value, len);
323 line[len - 1] = '\0';
324
325 return line;
326 }
327
Quentin Monnet9a5ab8b2017-10-23 09:24:13 -0700328 p_err("key '%s' not found in fdinfo", key);
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700329 free(line);
330 fclose(fdi);
331 return NULL;
332}
Quentin Monnetf05e2c32017-10-23 09:24:10 -0700333
Jakub Kicinskif412eed2018-05-03 18:37:16 -0700334void print_data_json(uint8_t *data, size_t len)
335{
336 unsigned int i;
337
338 jsonw_start_array(json_wtr);
339 for (i = 0; i < len; i++)
340 jsonw_printf(json_wtr, "%d", data[i]);
341 jsonw_end_array(json_wtr);
342}
343
Quentin Monnetf05e2c32017-10-23 09:24:10 -0700344void print_hex_data_json(uint8_t *data, size_t len)
345{
346 unsigned int i;
347
348 jsonw_start_array(json_wtr);
349 for (i = 0; i < len; i++)
350 jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
351 jsonw_end_array(json_wtr);
352}
Prashant Bhole4990f1f2017-11-08 13:55:48 +0900353
354int build_pinned_obj_table(struct pinned_obj_table *tab,
355 enum bpf_obj_type type)
356{
357 struct bpf_prog_info pinned_info = {};
358 struct pinned_obj *obj_node = NULL;
359 __u32 len = sizeof(pinned_info);
360 struct mntent *mntent = NULL;
361 enum bpf_obj_type objtype;
362 FILE *mntfile = NULL;
363 FTSENT *ftse = NULL;
364 FTS *fts = NULL;
365 int fd, err;
366
367 mntfile = setmntent("/proc/mounts", "r");
368 if (!mntfile)
369 return -1;
370
371 while ((mntent = getmntent(mntfile))) {
372 char *path[] = { mntent->mnt_dir, NULL };
373
374 if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
375 continue;
376
377 fts = fts_open(path, 0, NULL);
378 if (!fts)
379 continue;
380
381 while ((ftse = fts_read(fts))) {
382 if (!(ftse->fts_info & FTS_F))
383 continue;
384 fd = open_obj_pinned(ftse->fts_path);
385 if (fd < 0)
386 continue;
387
388 objtype = get_fd_type(fd);
389 if (objtype != type) {
390 close(fd);
391 continue;
392 }
393 memset(&pinned_info, 0, sizeof(pinned_info));
394 err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
395 if (err) {
396 close(fd);
397 continue;
398 }
399
400 obj_node = malloc(sizeof(*obj_node));
401 if (!obj_node) {
402 close(fd);
403 fts_close(fts);
404 fclose(mntfile);
405 return -1;
406 }
407
408 memset(obj_node, 0, sizeof(*obj_node));
409 obj_node->id = pinned_info.id;
410 obj_node->path = strdup(ftse->fts_path);
411 hash_add(tab->table, &obj_node->hash, obj_node->id);
412
413 close(fd);
414 }
415 fts_close(fts);
416 }
417 fclose(mntfile);
418 return 0;
419}
420
421void delete_pinned_obj_table(struct pinned_obj_table *tab)
422{
423 struct pinned_obj *obj;
424 struct hlist_node *tmp;
425 unsigned int bkt;
426
427 hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
428 hash_del(&obj->hash);
429 free(obj->path);
430 free(obj);
431 }
432}
Jakub Kicinski52262212017-12-27 18:39:10 -0800433
Jakub Kicinskif412eed2018-05-03 18:37:16 -0700434unsigned int get_page_size(void)
435{
436 static int result;
437
438 if (!result)
439 result = getpagesize();
440 return result;
441}
442
Jakub Kicinskie64d5252018-05-03 18:37:15 -0700443unsigned int get_possible_cpus(void)
444{
445 static unsigned int result;
446 char buf[128];
447 long int n;
448 char *ptr;
449 int fd;
450
451 if (result)
452 return result;
453
454 fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
455 if (fd < 0) {
456 p_err("can't open sysfs possible cpus");
457 exit(-1);
458 }
459
460 n = read(fd, buf, sizeof(buf));
461 if (n < 2) {
462 p_err("can't read sysfs possible cpus");
463 exit(-1);
464 }
465 close(fd);
466
467 if (n == sizeof(buf)) {
468 p_err("read sysfs possible cpus overflow");
469 exit(-1);
470 }
471
472 ptr = buf;
473 n = 0;
474 while (*ptr && *ptr != '\n') {
475 unsigned int a, b;
476
477 if (sscanf(ptr, "%u-%u", &a, &b) == 2) {
478 n += b - a + 1;
479
480 ptr = strchr(ptr, '-') + 1;
481 } else if (sscanf(ptr, "%u", &a) == 1) {
482 n++;
483 } else {
484 assert(0);
485 }
486
487 while (isdigit(*ptr))
488 ptr++;
489 if (*ptr == ',')
490 ptr++;
491 }
492
493 result = n;
494
495 return result;
496}
497
Jakub Kicinski52262212017-12-27 18:39:10 -0800498static char *
499ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
500{
501 struct stat st;
502 int err;
503
504 err = stat("/proc/self/ns/net", &st);
505 if (err) {
506 p_err("Can't stat /proc/self: %s", strerror(errno));
507 return NULL;
508 }
509
510 if (st.st_dev != ns_dev || st.st_ino != ns_ino)
511 return NULL;
512
513 return if_indextoname(ifindex, buf);
514}
515
Jiong Wange6593592018-01-16 16:05:21 -0800516static int read_sysfs_hex_int(char *path)
517{
518 char vendor_id_buf[8];
519 int len;
520 int fd;
521
522 fd = open(path, O_RDONLY);
523 if (fd < 0) {
524 p_err("Can't open %s: %s", path, strerror(errno));
525 return -1;
526 }
527
528 len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
529 close(fd);
530 if (len < 0) {
531 p_err("Can't read %s: %s", path, strerror(errno));
532 return -1;
533 }
534 if (len >= (int)sizeof(vendor_id_buf)) {
535 p_err("Value in %s too long", path);
536 return -1;
537 }
538
539 vendor_id_buf[len] = 0;
540
541 return strtol(vendor_id_buf, NULL, 0);
542}
543
544static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
545{
546 char full_path[64];
547
548 snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
549 devname, entry_name);
550
551 return read_sysfs_hex_int(full_path);
552}
553
554const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino)
555{
556 char devname[IF_NAMESIZE];
557 int vendor_id;
558 int device_id;
559
560 if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
561 p_err("Can't get net device name for ifindex %d: %s", ifindex,
562 strerror(errno));
563 return NULL;
564 }
565
566 vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
567 if (vendor_id < 0) {
568 p_err("Can't get device vendor id for %s", devname);
569 return NULL;
570 }
571
572 switch (vendor_id) {
573 case 0x19ee:
574 device_id = read_sysfs_netdev_hex_int(devname, "device");
575 if (device_id != 0x4000 &&
576 device_id != 0x6000 &&
577 device_id != 0x6003)
578 p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
579 return "NFP-6xxx";
580 default:
581 p_err("Can't get bfd arch name for device vendor id 0x%04x",
582 vendor_id);
583 return NULL;
584 }
585}
586
Jakub Kicinski52262212017-12-27 18:39:10 -0800587void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
588{
589 char name[IF_NAMESIZE];
590
591 if (!ifindex)
592 return;
593
594 printf(" dev ");
595 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
596 printf("%s", name);
597 else
598 printf("ifindex %u ns_dev %llu ns_ino %llu",
599 ifindex, ns_dev, ns_inode);
600}
601
602void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
603{
604 char name[IF_NAMESIZE];
605
606 if (!ifindex)
607 return;
608
609 jsonw_name(json_wtr, "dev");
610 jsonw_start_object(json_wtr);
611 jsonw_uint_field(json_wtr, "ifindex", ifindex);
612 jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
613 jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
614 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
615 jsonw_string_field(json_wtr, "ifname", name);
616 jsonw_end_object(json_wtr);
617}