• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * iplink_macvlan.c	macvlan/macvtap device support
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Patrick McHardy <kaber@trash.net>
10  *		Arnd Bergmann <arnd@arndb.de>
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/socket.h>
17 #include <linux/if_link.h>
18 #include <linux/if_ether.h>
19 
20 #include "rt_names.h"
21 #include "utils.h"
22 #include "ip_common.h"
23 
24 #define pfx_err(lu, ...) {               \
25 	fprintf(stderr, "%s: ", lu->id); \
26 	fprintf(stderr, __VA_ARGS__);    \
27 	fprintf(stderr, "\n");           \
28 }
29 
print_explain(struct link_util * lu,FILE * f)30 static void print_explain(struct link_util *lu, FILE *f)
31 {
32 	fprintf(f,
33 		"Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n"
34 		"\n"
35 		"MODE: private | vepa | bridge | passthru | source\n"
36 		"MODE_FLAG: null | nopromisc\n"
37 		"MODE_OPTS: for mode \"source\":\n"
38 		"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr>  ... ] ] | flush }\n",
39 		lu->id
40 	);
41 }
42 
explain(struct link_util * lu)43 static void explain(struct link_util *lu)
44 {
45 	print_explain(lu, stderr);
46 }
47 
48 
mode_arg(const char * arg)49 static int mode_arg(const char *arg)
50 {
51 	fprintf(stderr,
52 		"Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n",
53 		arg);
54 	return -1;
55 }
56 
flag_arg(const char * arg)57 static int flag_arg(const char *arg)
58 {
59 	fprintf(stderr,
60 		"Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n",
61 		arg);
62 	return -1;
63 }
64 
macvlan_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * n)65 static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
66 			  struct nlmsghdr *n)
67 {
68 	__u32 mode = 0;
69 	__u16 flags = 0;
70 	__u32 mac_mode = 0;
71 	int has_flags = 0;
72 	char mac[ETH_ALEN];
73 	struct rtattr *nmac;
74 
75 	while (argc > 0) {
76 		if (matches(*argv, "mode") == 0) {
77 			NEXT_ARG();
78 
79 			if (strcmp(*argv, "private") == 0)
80 				mode = MACVLAN_MODE_PRIVATE;
81 			else if (strcmp(*argv, "vepa") == 0)
82 				mode = MACVLAN_MODE_VEPA;
83 			else if (strcmp(*argv, "bridge") == 0)
84 				mode = MACVLAN_MODE_BRIDGE;
85 			else if (strcmp(*argv, "passthru") == 0)
86 				mode = MACVLAN_MODE_PASSTHRU;
87 			else if (strcmp(*argv, "source") == 0)
88 				mode = MACVLAN_MODE_SOURCE;
89 			else
90 				return mode_arg(*argv);
91 		} else if (matches(*argv, "flag") == 0) {
92 			NEXT_ARG();
93 
94 			if (strcmp(*argv, "nopromisc") == 0)
95 				flags |= MACVLAN_FLAG_NOPROMISC;
96 			else if (strcmp(*argv, "null") == 0)
97 				flags |= 0;
98 			else
99 				return flag_arg(*argv);
100 
101 			has_flags = 1;
102 
103 		} else if (matches(*argv, "macaddr") == 0) {
104 			NEXT_ARG();
105 
106 			if (strcmp(*argv, "add") == 0) {
107 				mac_mode = MACVLAN_MACADDR_ADD;
108 			} else if (strcmp(*argv, "del") == 0) {
109 				mac_mode = MACVLAN_MACADDR_DEL;
110 			} else if (strcmp(*argv, "set") == 0) {
111 				mac_mode = MACVLAN_MACADDR_SET;
112 			} else if (strcmp(*argv, "flush") == 0) {
113 				mac_mode = MACVLAN_MACADDR_FLUSH;
114 			} else {
115 				explain(lu);
116 				return -1;
117 			}
118 
119 			addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode);
120 
121 			if (mac_mode == MACVLAN_MACADDR_ADD ||
122 			    mac_mode == MACVLAN_MACADDR_DEL) {
123 				NEXT_ARG();
124 
125 				if (ll_addr_a2n(mac, sizeof(mac),
126 						*argv) != ETH_ALEN)
127 					return -1;
128 
129 				addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac,
130 					  ETH_ALEN);
131 			}
132 
133 			if (mac_mode == MACVLAN_MACADDR_SET) {
134 				nmac = addattr_nest(n, 1024,
135 						    IFLA_MACVLAN_MACADDR_DATA);
136 				while (NEXT_ARG_OK()) {
137 					NEXT_ARG_FWD();
138 
139 					if (ll_addr_a2n(mac, sizeof(mac),
140 							*argv) != ETH_ALEN) {
141 						PREV_ARG();
142 						break;
143 					}
144 
145 					addattr_l(n, 1024, IFLA_MACVLAN_MACADDR,
146 						  &mac, ETH_ALEN);
147 				}
148 				addattr_nest_end(n, nmac);
149 			}
150 		} else if (matches(*argv, "nopromisc") == 0) {
151 			flags |= MACVLAN_FLAG_NOPROMISC;
152 			has_flags = 1;
153 		} else if (matches(*argv, "help") == 0) {
154 			explain(lu);
155 			return -1;
156 		} else {
157 			pfx_err(lu, "unknown option \"%s\"?", *argv);
158 			explain(lu);
159 			return -1;
160 		}
161 		argc--, argv++;
162 	}
163 
164 	if (mode)
165 		addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
166 
167 	if (has_flags) {
168 		if (flags & MACVLAN_FLAG_NOPROMISC &&
169 		    mode != MACVLAN_MODE_PASSTHRU) {
170 			pfx_err(lu, "nopromisc flag only valid in passthru mode");
171 			explain(lu);
172 			return -1;
173 		}
174 		addattr16(n, 1024, IFLA_MACVLAN_FLAGS, flags);
175 	}
176 	return 0;
177 }
178 
macvlan_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])179 static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
180 {
181 	__u32 mode;
182 	__u16 flags;
183 	__u32 count;
184 	unsigned char *addr;
185 	int len;
186 	struct rtattr *rta;
187 
188 	if (!tb)
189 		return;
190 
191 	if (!tb[IFLA_MACVLAN_MODE] ||
192 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
193 		return;
194 
195 	mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]);
196 	print_string(PRINT_ANY,
197 		     "mode",
198 		     "mode %s ",
199 		     mode == MACVLAN_MODE_PRIVATE ? "private"
200 		     : mode == MACVLAN_MODE_VEPA    ? "vepa"
201 		     : mode == MACVLAN_MODE_BRIDGE  ? "bridge"
202 		     : mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
203 		     : mode == MACVLAN_MODE_SOURCE  ? "source"
204 		     :				 "unknown");
205 
206 	if (!tb[IFLA_MACVLAN_FLAGS] ||
207 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16))
208 		flags = 0;
209 	else
210 		flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
211 
212 	if (flags & MACVLAN_FLAG_NOPROMISC)
213 		print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
214 
215 	/* in source mode, there are more options to print */
216 
217 	if (mode != MACVLAN_MODE_SOURCE)
218 		return;
219 
220 	if (!tb[IFLA_MACVLAN_MACADDR_COUNT] ||
221 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32))
222 		return;
223 
224 	count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
225 	print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count);
226 
227 	if (!tb[IFLA_MACVLAN_MACADDR_DATA])
228 		return;
229 
230 	rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]);
231 	len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]);
232 
233 	open_json_array(PRINT_JSON, "macaddr_data");
234 	for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
235 		if (rta->rta_type != IFLA_MACVLAN_MACADDR ||
236 		    RTA_PAYLOAD(rta) < 6)
237 			continue;
238 		addr = RTA_DATA(rta);
239 		if (is_json_context()) {
240 			SPRINT_BUF(b1);
241 
242 			snprintf(b1, sizeof(b1),
243 				 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0],
244 				 addr[1], addr[2], addr[3], addr[4], addr[5]);
245 			print_string(PRINT_JSON, NULL, NULL, b1);
246 		} else {
247 			fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
248 				addr[1], addr[2], addr[3], addr[4], addr[5]);
249 		}
250 	}
251 	close_json_array(PRINT_JSON, NULL);
252 }
253 
macvlan_print_help(struct link_util * lu,int argc,char ** argv,FILE * f)254 static void macvlan_print_help(struct link_util *lu, int argc, char **argv,
255 			       FILE *f)
256 {
257 	print_explain(lu, f);
258 }
259 
260 struct link_util macvlan_link_util = {
261 	.id		= "macvlan",
262 	.maxattr	= IFLA_MACVLAN_MAX,
263 	.parse_opt	= macvlan_parse_opt,
264 	.print_opt	= macvlan_print_opt,
265 	.print_help	= macvlan_print_help,
266 };
267 
268 struct link_util macvtap_link_util = {
269 	.id		= "macvtap",
270 	.maxattr	= IFLA_MACVLAN_MAX,
271 	.parse_opt	= macvlan_parse_opt,
272 	.print_opt	= macvlan_print_opt,
273 	.print_help	= macvlan_print_help,
274 };
275