mirror of
				https://git.proxmox.com/git/mirror_iproute2
				synced 2025-11-04 06:22:48 +00:00 
			
		
		
		
	Schema:
{
    "mode": {
        "type": "string",
        "attr": "IFLA_MACVLAN_MODE"
    },
    "nopromisc": {
        "type": "bool",
        "attr": "MACVLAN_FLAG_NOPROMISC"
    },
    "macaddr_count": {
        "type": "int",
        "attr": "IFLA_MACVLAN_MACADDR_COUNT"
    },
    "macaddr_data": {
        "type": "array",
        "attr": "IFLA_MACVLAN_MACADDR_DATA",
        "array": [
            {
                "type": "string"
            }
        ]
    },
}
$ ip link add name peth0 link eth0 type macvlan
$ ip -details -json link show peth0
[{
        "ifindex": 26,
        "ifname": "peth0",
        "link": "eth0",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "7a:84:48:3e:7b:1c",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "macvlan",
            "info_data": {
                "mode": "vepa"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]
Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
		
	
			
		
			
				
	
	
		
			275 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * iplink_macvlan.c	macvlan/macvtap device support
 | 
						|
 *
 | 
						|
 *              This program is free software; you can redistribute it and/or
 | 
						|
 *              modify it under the terms of the GNU General Public License
 | 
						|
 *              as published by the Free Software Foundation; either version
 | 
						|
 *              2 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * Authors:     Patrick McHardy <kaber@trash.net>
 | 
						|
 *		Arnd Bergmann <arnd@arndb.de>
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <linux/if_link.h>
 | 
						|
#include <linux/if_ether.h>
 | 
						|
 | 
						|
#include "rt_names.h"
 | 
						|
#include "utils.h"
 | 
						|
#include "ip_common.h"
 | 
						|
 | 
						|
#define pfx_err(lu, ...) {               \
 | 
						|
	fprintf(stderr, "%s: ", lu->id); \
 | 
						|
	fprintf(stderr, __VA_ARGS__);    \
 | 
						|
	fprintf(stderr, "\n");           \
 | 
						|
}
 | 
						|
 | 
						|
static void print_explain(struct link_util *lu, FILE *f)
 | 
						|
{
 | 
						|
	fprintf(f,
 | 
						|
		"Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n"
 | 
						|
		"\n"
 | 
						|
		"MODE: private | vepa | bridge | passthru | source\n"
 | 
						|
		"MODE_FLAG: null | nopromisc\n"
 | 
						|
		"MODE_OPTS: for mode \"source\":\n"
 | 
						|
		"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr>  ... ] ] | flush }\n",
 | 
						|
		lu->id
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
static void explain(struct link_util *lu)
 | 
						|
{
 | 
						|
	print_explain(lu, stderr);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int mode_arg(const char *arg)
 | 
						|
{
 | 
						|
	fprintf(stderr,
 | 
						|
		"Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n",
 | 
						|
		arg);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int flag_arg(const char *arg)
 | 
						|
{
 | 
						|
	fprintf(stderr,
 | 
						|
		"Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n",
 | 
						|
		arg);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
 | 
						|
			  struct nlmsghdr *n)
 | 
						|
{
 | 
						|
	__u32 mode = 0;
 | 
						|
	__u16 flags = 0;
 | 
						|
	__u32 mac_mode = 0;
 | 
						|
	int has_flags = 0;
 | 
						|
	char mac[ETH_ALEN];
 | 
						|
	struct rtattr *nmac;
 | 
						|
 | 
						|
	while (argc > 0) {
 | 
						|
		if (matches(*argv, "mode") == 0) {
 | 
						|
			NEXT_ARG();
 | 
						|
 | 
						|
			if (strcmp(*argv, "private") == 0)
 | 
						|
				mode = MACVLAN_MODE_PRIVATE;
 | 
						|
			else if (strcmp(*argv, "vepa") == 0)
 | 
						|
				mode = MACVLAN_MODE_VEPA;
 | 
						|
			else if (strcmp(*argv, "bridge") == 0)
 | 
						|
				mode = MACVLAN_MODE_BRIDGE;
 | 
						|
			else if (strcmp(*argv, "passthru") == 0)
 | 
						|
				mode = MACVLAN_MODE_PASSTHRU;
 | 
						|
			else if (strcmp(*argv, "source") == 0)
 | 
						|
				mode = MACVLAN_MODE_SOURCE;
 | 
						|
			else
 | 
						|
				return mode_arg(*argv);
 | 
						|
		} else if (matches(*argv, "flag") == 0) {
 | 
						|
			NEXT_ARG();
 | 
						|
 | 
						|
			if (strcmp(*argv, "nopromisc") == 0)
 | 
						|
				flags |= MACVLAN_FLAG_NOPROMISC;
 | 
						|
			else if (strcmp(*argv, "null") == 0)
 | 
						|
				flags |= 0;
 | 
						|
			else
 | 
						|
				return flag_arg(*argv);
 | 
						|
 | 
						|
			has_flags = 1;
 | 
						|
 | 
						|
		} else if (matches(*argv, "macaddr") == 0) {
 | 
						|
			NEXT_ARG();
 | 
						|
 | 
						|
			if (strcmp(*argv, "add") == 0) {
 | 
						|
				mac_mode = MACVLAN_MACADDR_ADD;
 | 
						|
			} else if (strcmp(*argv, "del") == 0) {
 | 
						|
				mac_mode = MACVLAN_MACADDR_DEL;
 | 
						|
			} else if (strcmp(*argv, "set") == 0) {
 | 
						|
				mac_mode = MACVLAN_MACADDR_SET;
 | 
						|
			} else if (strcmp(*argv, "flush") == 0) {
 | 
						|
				mac_mode = MACVLAN_MACADDR_FLUSH;
 | 
						|
			} else {
 | 
						|
				explain(lu);
 | 
						|
				return -1;
 | 
						|
			}
 | 
						|
 | 
						|
			addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode);
 | 
						|
 | 
						|
			if (mac_mode == MACVLAN_MACADDR_ADD ||
 | 
						|
			    mac_mode == MACVLAN_MACADDR_DEL) {
 | 
						|
				NEXT_ARG();
 | 
						|
 | 
						|
				if (ll_addr_a2n(mac, sizeof(mac),
 | 
						|
						*argv) != ETH_ALEN)
 | 
						|
					return -1;
 | 
						|
 | 
						|
				addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac,
 | 
						|
					  ETH_ALEN);
 | 
						|
			}
 | 
						|
 | 
						|
			if (mac_mode == MACVLAN_MACADDR_SET) {
 | 
						|
				nmac = addattr_nest(n, 1024,
 | 
						|
						    IFLA_MACVLAN_MACADDR_DATA);
 | 
						|
				while (NEXT_ARG_OK()) {
 | 
						|
					NEXT_ARG_FWD();
 | 
						|
 | 
						|
					if (ll_addr_a2n(mac, sizeof(mac),
 | 
						|
							*argv) != ETH_ALEN) {
 | 
						|
						PREV_ARG();
 | 
						|
						break;
 | 
						|
					}
 | 
						|
 | 
						|
					addattr_l(n, 1024, IFLA_MACVLAN_MACADDR,
 | 
						|
						  &mac, ETH_ALEN);
 | 
						|
				}
 | 
						|
				addattr_nest_end(n, nmac);
 | 
						|
			}
 | 
						|
		} else if (matches(*argv, "nopromisc") == 0) {
 | 
						|
			flags |= MACVLAN_FLAG_NOPROMISC;
 | 
						|
			has_flags = 1;
 | 
						|
		} else if (matches(*argv, "help") == 0) {
 | 
						|
			explain(lu);
 | 
						|
			return -1;
 | 
						|
		} else {
 | 
						|
			pfx_err(lu, "unknown option \"%s\"?", *argv);
 | 
						|
			explain(lu);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		argc--, argv++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mode)
 | 
						|
		addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
 | 
						|
 | 
						|
	if (has_flags) {
 | 
						|
		if (flags & MACVLAN_FLAG_NOPROMISC &&
 | 
						|
		    mode != MACVLAN_MODE_PASSTHRU) {
 | 
						|
			pfx_err(lu, "nopromisc flag only valid in passthru mode");
 | 
						|
			explain(lu);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		addattr16(n, 1024, IFLA_MACVLAN_FLAGS, flags);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 | 
						|
{
 | 
						|
	__u32 mode;
 | 
						|
	__u16 flags;
 | 
						|
	__u32 count;
 | 
						|
	unsigned char *addr;
 | 
						|
	int len;
 | 
						|
	struct rtattr *rta;
 | 
						|
 | 
						|
	if (!tb)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (!tb[IFLA_MACVLAN_MODE] ||
 | 
						|
	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
 | 
						|
		return;
 | 
						|
 | 
						|
	mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]);
 | 
						|
	print_string(PRINT_ANY,
 | 
						|
		     "mode",
 | 
						|
		     "mode %s ",
 | 
						|
		     mode == MACVLAN_MODE_PRIVATE ? "private"
 | 
						|
		     : mode == MACVLAN_MODE_VEPA    ? "vepa"
 | 
						|
		     : mode == MACVLAN_MODE_BRIDGE  ? "bridge"
 | 
						|
		     : mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
 | 
						|
		     : mode == MACVLAN_MODE_SOURCE  ? "source"
 | 
						|
		     :				 "unknown");
 | 
						|
 | 
						|
	if (!tb[IFLA_MACVLAN_FLAGS] ||
 | 
						|
	    RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16))
 | 
						|
		flags = 0;
 | 
						|
	else
 | 
						|
		flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
 | 
						|
 | 
						|
	if (flags & MACVLAN_FLAG_NOPROMISC)
 | 
						|
		print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
 | 
						|
 | 
						|
	/* in source mode, there are more options to print */
 | 
						|
 | 
						|
	if (mode != MACVLAN_MODE_SOURCE)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (!tb[IFLA_MACVLAN_MACADDR_COUNT] ||
 | 
						|
	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32))
 | 
						|
		return;
 | 
						|
 | 
						|
	count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
 | 
						|
	print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count);
 | 
						|
 | 
						|
	if (!tb[IFLA_MACVLAN_MACADDR_DATA])
 | 
						|
		return;
 | 
						|
 | 
						|
	rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]);
 | 
						|
	len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]);
 | 
						|
 | 
						|
	open_json_array(PRINT_JSON, "macaddr_data");
 | 
						|
	for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
 | 
						|
		if (rta->rta_type != IFLA_MACVLAN_MACADDR ||
 | 
						|
		    RTA_PAYLOAD(rta) < 6)
 | 
						|
			continue;
 | 
						|
		addr = RTA_DATA(rta);
 | 
						|
		if (is_json_context()) {
 | 
						|
			SPRINT_BUF(b1);
 | 
						|
 | 
						|
			snprintf(b1, sizeof(b1),
 | 
						|
				 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0],
 | 
						|
				 addr[1], addr[2], addr[3], addr[4], addr[5]);
 | 
						|
			print_string(PRINT_JSON, NULL, NULL, b1);
 | 
						|
		} else {
 | 
						|
			fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
 | 
						|
				addr[1], addr[2], addr[3], addr[4], addr[5]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	close_json_array(PRINT_JSON, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void macvlan_print_help(struct link_util *lu, int argc, char **argv,
 | 
						|
			       FILE *f)
 | 
						|
{
 | 
						|
	print_explain(lu, f);
 | 
						|
}
 | 
						|
 | 
						|
struct link_util macvlan_link_util = {
 | 
						|
	.id		= "macvlan",
 | 
						|
	.maxattr	= IFLA_MACVLAN_MAX,
 | 
						|
	.parse_opt	= macvlan_parse_opt,
 | 
						|
	.print_opt	= macvlan_print_opt,
 | 
						|
	.print_help	= macvlan_print_help,
 | 
						|
};
 | 
						|
 | 
						|
struct link_util macvtap_link_util = {
 | 
						|
	.id		= "macvtap",
 | 
						|
	.maxattr	= IFLA_MACVLAN_MAX,
 | 
						|
	.parse_opt	= macvlan_parse_opt,
 | 
						|
	.print_opt	= macvlan_print_opt,
 | 
						|
	.print_help	= macvlan_print_help,
 | 
						|
};
 |