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