Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  1/*
  2 * Copyright (C) 2017 Netronome Systems, Inc.
  3 *
  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
 36#include <errno.h>
 37#include <fcntl.h>
 38#include <fts.h>
 39#include <libgen.h>
 40#include <mntent.h>
 41#include <stdbool.h>
 42#include <stdio.h>
 43#include <stdlib.h>
 44#include <string.h>
 45#include <unistd.h>
 46#include <linux/limits.h>
 47#include <linux/magic.h>
 48#include <net/if.h>
 49#include <sys/mount.h>
 50#include <sys/stat.h>
 51#include <sys/types.h>
 52#include <sys/vfs.h>
 53
 54#include <bpf.h>
 55
 56#include "main.h"
 57
 58#ifndef BPF_FS_MAGIC
 59#define BPF_FS_MAGIC		0xcafe4a11
 60#endif
 61
 62void p_err(const char *fmt, ...)
 63{
 64	va_list ap;
 65
 66	va_start(ap, fmt);
 67	if (json_output) {
 68		jsonw_start_object(json_wtr);
 69		jsonw_name(json_wtr, "error");
 70		jsonw_vprintf_enquote(json_wtr, fmt, ap);
 71		jsonw_end_object(json_wtr);
 72	} else {
 73		fprintf(stderr, "Error: ");
 74		vfprintf(stderr, fmt, ap);
 75		fprintf(stderr, "\n");
 76	}
 77	va_end(ap);
 78}
 79
 80void p_info(const char *fmt, ...)
 81{
 82	va_list ap;
 83
 84	if (json_output)
 85		return;
 86
 87	va_start(ap, fmt);
 88	vfprintf(stderr, fmt, ap);
 89	fprintf(stderr, "\n");
 90	va_end(ap);
 91}
 92
 93static bool is_bpffs(char *path)
 94{
 95	struct statfs st_fs;
 96
 97	if (statfs(path, &st_fs) < 0)
 98		return false;
 99
100	return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
101}
102
103static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
104{
105	bool bind_done = false;
106
107	while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
108		if (errno != EINVAL || bind_done) {
109			snprintf(buff, bufflen,
110				 "mount --make-private %s failed: %s",
111				 target, strerror(errno));
112			return -1;
113		}
114
115		if (mount(target, target, "none", MS_BIND, NULL)) {
116			snprintf(buff, bufflen,
117				 "mount --bind %s %s failed: %s",
118				 target, target, strerror(errno));
119			return -1;
120		}
121
122		bind_done = true;
123	}
124
125	if (mount("bpf", target, "bpf", 0, "mode=0700")) {
126		snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s",
127			 target, strerror(errno));
128		return -1;
129	}
130
131	return 0;
132}
133
134int open_obj_pinned(char *path)
135{
136	int fd;
137
138	fd = bpf_obj_get(path);
139	if (fd < 0) {
140		p_err("bpf obj get (%s): %s", path,
141		      errno == EACCES && !is_bpffs(dirname(path)) ?
142		    "directory not in bpf file system (bpffs)" :
143		    strerror(errno));
144		return -1;
145	}
146
147	return fd;
148}
149
150int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
151{
152	enum bpf_obj_type type;
153	int fd;
154
155	fd = open_obj_pinned(path);
156	if (fd < 0)
157		return -1;
158
159	type = get_fd_type(fd);
160	if (type < 0) {
161		close(fd);
162		return type;
163	}
164	if (type != exp_type) {
165		p_err("incorrect object type: %s", get_fd_type_name(type));
166		close(fd);
167		return -1;
168	}
169
170	return fd;
171}
172
173int do_pin_fd(int fd, const char *name)
174{
175	char err_str[ERR_MAX_LEN];
176	char *file;
177	char *dir;
178	int err = 0;
179
180	err = bpf_obj_pin(fd, name);
181	if (!err)
182		goto out;
183
184	file = malloc(strlen(name) + 1);
185	strcpy(file, name);
186	dir = dirname(file);
187
188	if (errno != EPERM || is_bpffs(dir)) {
189		p_err("can't pin the object (%s): %s", name, strerror(errno));
190		goto out_free;
191	}
192
193	/* Attempt to mount bpffs, then retry pinning. */
194	err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
195	if (!err) {
196		err = bpf_obj_pin(fd, name);
197		if (err)
198			p_err("can't pin the object (%s): %s", name,
199			      strerror(errno));
200	} else {
201		err_str[ERR_MAX_LEN - 1] = '\0';
202		p_err("can't mount BPF file system to pin the object (%s): %s",
203		      name, err_str);
204	}
205
206out_free:
207	free(file);
208out:
209	return err;
210}
211
212int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
213{
214	unsigned int id;
215	char *endptr;
216	int err;
217	int fd;
218
219	if (!is_prefix(*argv, "id")) {
220		p_err("expected 'id' got %s", *argv);
221		return -1;
222	}
223	NEXT_ARG();
224
225	id = strtoul(*argv, &endptr, 0);
226	if (*endptr) {
227		p_err("can't parse %s as ID", *argv);
228		return -1;
229	}
230	NEXT_ARG();
231
232	if (argc != 1)
233		usage();
234
235	fd = get_fd_by_id(id);
236	if (fd < 0) {
237		p_err("can't get prog by id (%u): %s", id, strerror(errno));
238		return -1;
239	}
240
241	err = do_pin_fd(fd, *argv);
242
243	close(fd);
244	return err;
245}
246
247const char *get_fd_type_name(enum bpf_obj_type type)
248{
249	static const char * const names[] = {
250		[BPF_OBJ_UNKNOWN]	= "unknown",
251		[BPF_OBJ_PROG]		= "prog",
252		[BPF_OBJ_MAP]		= "map",
253	};
254
255	if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
256		return names[BPF_OBJ_UNKNOWN];
257
258	return names[type];
259}
260
261int get_fd_type(int fd)
262{
263	char path[PATH_MAX];
264	char buf[512];
265	ssize_t n;
266
267	snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
268
269	n = readlink(path, buf, sizeof(buf));
270	if (n < 0) {
271		p_err("can't read link type: %s", strerror(errno));
272		return -1;
273	}
274	if (n == sizeof(path)) {
275		p_err("can't read link type: path too long!");
276		return -1;
277	}
278
279	if (strstr(buf, "bpf-map"))
280		return BPF_OBJ_MAP;
281	else if (strstr(buf, "bpf-prog"))
282		return BPF_OBJ_PROG;
283
284	return BPF_OBJ_UNKNOWN;
285}
286
287char *get_fdinfo(int fd, const char *key)
288{
289	char path[PATH_MAX];
290	char *line = NULL;
291	size_t line_n = 0;
292	ssize_t n;
293	FILE *fdi;
294
295	snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
296
297	fdi = fopen(path, "r");
298	if (!fdi) {
299		p_err("can't open fdinfo: %s", strerror(errno));
300		return NULL;
301	}
302
303	while ((n = getline(&line, &line_n, fdi))) {
304		char *value;
305		int len;
306
307		if (!strstr(line, key))
308			continue;
309
310		fclose(fdi);
311
312		value = strchr(line, '\t');
313		if (!value || !value[1]) {
314			p_err("malformed fdinfo!?");
315			free(line);
316			return NULL;
317		}
318		value++;
319
320		len = strlen(value);
321		memmove(line, value, len);
322		line[len - 1] = '\0';
323
324		return line;
325	}
326
327	p_err("key '%s' not found in fdinfo", key);
328	free(line);
329	fclose(fdi);
330	return NULL;
331}
332
333void print_hex_data_json(uint8_t *data, size_t len)
334{
335	unsigned int i;
336
337	jsonw_start_array(json_wtr);
338	for (i = 0; i < len; i++)
339		jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
340	jsonw_end_array(json_wtr);
341}
342
343int build_pinned_obj_table(struct pinned_obj_table *tab,
344			   enum bpf_obj_type type)
345{
346	struct bpf_prog_info pinned_info = {};
347	struct pinned_obj *obj_node = NULL;
348	__u32 len = sizeof(pinned_info);
349	struct mntent *mntent = NULL;
350	enum bpf_obj_type objtype;
351	FILE *mntfile = NULL;
352	FTSENT *ftse = NULL;
353	FTS *fts = NULL;
354	int fd, err;
355
356	mntfile = setmntent("/proc/mounts", "r");
357	if (!mntfile)
358		return -1;
359
360	while ((mntent = getmntent(mntfile))) {
361		char *path[] = { mntent->mnt_dir, NULL };
362
363		if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
364			continue;
365
366		fts = fts_open(path, 0, NULL);
367		if (!fts)
368			continue;
369
370		while ((ftse = fts_read(fts))) {
371			if (!(ftse->fts_info & FTS_F))
372				continue;
373			fd = open_obj_pinned(ftse->fts_path);
374			if (fd < 0)
375				continue;
376
377			objtype = get_fd_type(fd);
378			if (objtype != type) {
379				close(fd);
380				continue;
381			}
382			memset(&pinned_info, 0, sizeof(pinned_info));
383			err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
384			if (err) {
385				close(fd);
386				continue;
387			}
388
389			obj_node = malloc(sizeof(*obj_node));
390			if (!obj_node) {
391				close(fd);
392				fts_close(fts);
393				fclose(mntfile);
394				return -1;
395			}
396
397			memset(obj_node, 0, sizeof(*obj_node));
398			obj_node->id = pinned_info.id;
399			obj_node->path = strdup(ftse->fts_path);
400			hash_add(tab->table, &obj_node->hash, obj_node->id);
401
402			close(fd);
403		}
404		fts_close(fts);
405	}
406	fclose(mntfile);
407	return 0;
408}
409
410void delete_pinned_obj_table(struct pinned_obj_table *tab)
411{
412	struct pinned_obj *obj;
413	struct hlist_node *tmp;
414	unsigned int bkt;
415
416	hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
417		hash_del(&obj->hash);
418		free(obj->path);
419		free(obj);
420	}
421}
422
423static char *
424ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
425{
426	struct stat st;
427	int err;
428
429	err = stat("/proc/self/ns/net", &st);
430	if (err) {
431		p_err("Can't stat /proc/self: %s", strerror(errno));
432		return NULL;
433	}
434
435	if (st.st_dev != ns_dev || st.st_ino != ns_ino)
436		return NULL;
437
438	return if_indextoname(ifindex, buf);
439}
440
441static int read_sysfs_hex_int(char *path)
442{
443	char vendor_id_buf[8];
444	int len;
445	int fd;
446
447	fd = open(path, O_RDONLY);
448	if (fd < 0) {
449		p_err("Can't open %s: %s", path, strerror(errno));
450		return -1;
451	}
452
453	len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
454	close(fd);
455	if (len < 0) {
456		p_err("Can't read %s: %s", path, strerror(errno));
457		return -1;
458	}
459	if (len >= (int)sizeof(vendor_id_buf)) {
460		p_err("Value in %s too long", path);
461		return -1;
462	}
463
464	vendor_id_buf[len] = 0;
465
466	return strtol(vendor_id_buf, NULL, 0);
467}
468
469static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
470{
471	char full_path[64];
472
473	snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
474		 devname, entry_name);
475
476	return read_sysfs_hex_int(full_path);
477}
478
479const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino)
480{
481	char devname[IF_NAMESIZE];
482	int vendor_id;
483	int device_id;
484
485	if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
486		p_err("Can't get net device name for ifindex %d: %s", ifindex,
487		      strerror(errno));
488		return NULL;
489	}
490
491	vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
492	if (vendor_id < 0) {
493		p_err("Can't get device vendor id for %s", devname);
494		return NULL;
495	}
496
497	switch (vendor_id) {
498	case 0x19ee:
499		device_id = read_sysfs_netdev_hex_int(devname, "device");
500		if (device_id != 0x4000 &&
501		    device_id != 0x6000 &&
502		    device_id != 0x6003)
503			p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
504		return "NFP-6xxx";
505	default:
506		p_err("Can't get bfd arch name for device vendor id 0x%04x",
507		      vendor_id);
508		return NULL;
509	}
510}
511
512void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
513{
514	char name[IF_NAMESIZE];
515
516	if (!ifindex)
517		return;
518
519	printf(" dev ");
520	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
521		printf("%s", name);
522	else
523		printf("ifindex %u ns_dev %llu ns_ino %llu",
524		       ifindex, ns_dev, ns_inode);
525}
526
527void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
528{
529	char name[IF_NAMESIZE];
530
531	if (!ifindex)
532		return;
533
534	jsonw_name(json_wtr, "dev");
535	jsonw_start_object(json_wtr);
536	jsonw_uint_field(json_wtr, "ifindex", ifindex);
537	jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
538	jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
539	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
540		jsonw_string_field(json_wtr, "ifname", name);
541	jsonw_end_object(json_wtr);
542}