blob: b9a146f2711867409b945c59d7cf51cb0cf0f25c [file] [log] [blame]
Arnd Bergmannd63a9b22009-12-26 11:22:57 -08001/*
Phil Sutter541f1b32015-09-25 14:09:49 +02002 * iplink_macvlan.c macvlan/macvtap device support
Arnd Bergmannd63a9b22009-12-26 11:22:57 -08003 *
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>
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010018#include <linux/if_ether.h>
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080019
20#include "rt_names.h"
21#include "utils.h"
22#include "ip_common.h"
23
Phil Sutter541f1b32015-09-25 14:09:49 +020024#define pfx_err(lu, ...) { \
25 fprintf(stderr, "%s: ", lu->id); \
26 fprintf(stderr, __VA_ARGS__); \
27 fprintf(stderr, "\n"); \
28}
29
30static void print_explain(struct link_util *lu, FILE *f)
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080031{
vadimk561e6502014-09-30 08:17:31 +030032 fprintf(f,
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010033 "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n"
34 "MODE: private | vepa | bridge | passthru | source\n"
35 "MODE_FLAG: null | nopromisc\n"
36 "MODE_OPTS: for mode \"source\":\n"
37 "\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | flush }\n",
Phil Sutter541f1b32015-09-25 14:09:49 +020038 lu->id
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080039 );
40}
41
Phil Sutter541f1b32015-09-25 14:09:49 +020042static void explain(struct link_util *lu)
vadimk561e6502014-09-30 08:17:31 +030043{
Phil Sutter541f1b32015-09-25 14:09:49 +020044 print_explain(lu, stderr);
vadimk561e6502014-09-30 08:17:31 +030045}
46
michael-dev@fami-braun.def33b7272016-09-25 21:08:55 +020047
Stephen Hemmingerf3f339e2016-10-26 11:19:11 -070048static int mode_arg(const char *arg)
michael-dev@fami-braun.def33b7272016-09-25 21:08:55 +020049{
Stephen Hemminger74093342016-10-12 15:23:27 -070050 fprintf(stderr,
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010051 "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n",
52 arg);
53 return -1;
54}
55
56static int flag_arg(const char *arg)
57{
58 fprintf(stderr,
59 "Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n",
Stephen Hemminger56f5daa2016-03-21 11:52:19 -070060 arg);
61 return -1;
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080062}
63
64static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
65 struct nlmsghdr *n)
66{
Phil Sutter3cf8ba52015-09-25 14:09:50 +020067 __u32 mode = 0;
68 __u16 flags = 0;
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010069 __u32 mac_mode = 0;
70 int has_flags = 0;
71 char mac[ETH_ALEN];
72 struct rtattr *nmac;
Phil Sutter3cf8ba52015-09-25 14:09:50 +020073
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080074 while (argc > 0) {
75 if (matches(*argv, "mode") == 0) {
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080076 NEXT_ARG();
77
78 if (strcmp(*argv, "private") == 0)
79 mode = MACVLAN_MODE_PRIVATE;
80 else if (strcmp(*argv, "vepa") == 0)
81 mode = MACVLAN_MODE_VEPA;
82 else if (strcmp(*argv, "bridge") == 0)
83 mode = MACVLAN_MODE_BRIDGE;
Sridhar Samudralaf0612d52011-03-16 17:01:58 -070084 else if (strcmp(*argv, "passthru") == 0)
85 mode = MACVLAN_MODE_PASSTHRU;
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010086 else if (strcmp(*argv, "source") == 0)
87 mode = MACVLAN_MODE_SOURCE;
Arnd Bergmannd63a9b22009-12-26 11:22:57 -080088 else
Phil Sutter541f1b32015-09-25 14:09:49 +020089 return mode_arg(*argv);
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +010090 } else if (matches(*argv, "flag") == 0) {
91 NEXT_ARG();
92
93 if (strcmp(*argv, "nopromisc") == 0)
94 flags |= MACVLAN_FLAG_NOPROMISC;
95 else if (strcmp(*argv, "null") == 0)
96 flags |= 0;
97 else
98 return flag_arg(*argv);
99
100 has_flags = 1;
101
102 } else if (matches(*argv, "macaddr") == 0) {
103 NEXT_ARG();
104
105 if (strcmp(*argv, "add") == 0) {
106 mac_mode = MACVLAN_MACADDR_ADD;
107 } else if (strcmp(*argv, "del") == 0) {
108 mac_mode = MACVLAN_MACADDR_DEL;
109 } else if (strcmp(*argv, "set") == 0) {
110 mac_mode = MACVLAN_MACADDR_SET;
111 } else if (strcmp(*argv, "flush") == 0) {
112 mac_mode = MACVLAN_MACADDR_FLUSH;
113 } else {
114 explain(lu);
115 return -1;
116 }
117
118 addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode);
119
120 if (mac_mode == MACVLAN_MACADDR_ADD ||
121 mac_mode == MACVLAN_MACADDR_DEL) {
122 NEXT_ARG();
123
124 if (ll_addr_a2n(mac, sizeof(mac),
125 *argv) != ETH_ALEN)
126 return -1;
127
128 addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac,
129 ETH_ALEN);
130 }
131
132 if (mac_mode == MACVLAN_MACADDR_SET) {
133 nmac = addattr_nest(n, 1024,
134 IFLA_MACVLAN_MACADDR_DATA);
135 while (NEXT_ARG_OK()) {
136 NEXT_ARG_FWD();
137
138 if (ll_addr_a2n(mac, sizeof(mac),
139 *argv) != ETH_ALEN) {
140 PREV_ARG();
141 break;
142 }
143
144 addattr_l(n, 1024, IFLA_MACVLAN_MACADDR,
145 &mac, ETH_ALEN);
146 }
147 addattr_nest_end(n, nmac);
148 }
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200149 } else if (matches(*argv, "nopromisc") == 0) {
150 flags |= MACVLAN_FLAG_NOPROMISC;
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100151 has_flags = 1;
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800152 } else if (matches(*argv, "help") == 0) {
Phil Sutter541f1b32015-09-25 14:09:49 +0200153 explain(lu);
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800154 return -1;
155 } else {
Phil Sutter541f1b32015-09-25 14:09:49 +0200156 pfx_err(lu, "unknown option \"%s\"?", *argv);
157 explain(lu);
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800158 return -1;
159 }
160 argc--, argv++;
161 }
162
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200163 if (mode)
164 addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
165
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100166 if (has_flags) {
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200167 if (flags & MACVLAN_FLAG_NOPROMISC &&
168 mode != MACVLAN_MODE_PASSTHRU) {
169 pfx_err(lu, "nopromisc flag only valid in passthru mode");
170 explain(lu);
171 return -1;
172 }
173 addattr16(n, 1024, IFLA_MACVLAN_FLAGS, flags);
174 }
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800175 return 0;
176}
177
178static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
179{
180 __u32 mode;
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200181 __u16 flags;
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100182 __u32 count;
183 unsigned char *addr;
184 int len;
185 struct rtattr *rta;
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800186
187 if (!tb)
188 return;
189
190 if (!tb[IFLA_MACVLAN_MODE] ||
191 RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
192 return;
193
Lutz Jaenicke7dc04812013-08-29 09:50:28 +0200194 mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]);
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100195 fprintf(f, "mode %s ",
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800196 mode == MACVLAN_MODE_PRIVATE ? "private"
197 : mode == MACVLAN_MODE_VEPA ? "vepa"
198 : mode == MACVLAN_MODE_BRIDGE ? "bridge"
Sridhar Samudralaf0612d52011-03-16 17:01:58 -0700199 : mode == MACVLAN_MODE_PASSTHRU ? "passthru"
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100200 : mode == MACVLAN_MODE_SOURCE ? "source"
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800201 : "unknown");
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200202
203 if (!tb[IFLA_MACVLAN_FLAGS] ||
204 RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16))
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100205 flags = 0;
206 else
207 flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200208
Phil Sutter3cf8ba52015-09-25 14:09:50 +0200209 if (flags & MACVLAN_FLAG_NOPROMISC)
210 fprintf(f, "nopromisc ");
michael-dev@fami-braun.deaa1b44c2016-11-22 11:59:13 +0100211
212 /* in source mode, there are more options to print */
213
214 if (mode != MACVLAN_MODE_SOURCE)
215 return;
216
217 if (!tb[IFLA_MACVLAN_MACADDR_COUNT] ||
218 RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32))
219 return;
220
221 count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
222 fprintf(f, "remotes (%d) ", count);
223
224 if (!tb[IFLA_MACVLAN_MACADDR_DATA])
225 return;
226
227 rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]);
228 len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]);
229
230 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
231 if (rta->rta_type != IFLA_MACVLAN_MACADDR ||
232 RTA_PAYLOAD(rta) < 6)
233 continue;
234 addr = RTA_DATA(rta);
235 fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
236 addr[1], addr[2], addr[3], addr[4], addr[5]);
237 }
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800238}
239
vadimk561e6502014-09-30 08:17:31 +0300240static void macvlan_print_help(struct link_util *lu, int argc, char **argv,
241 FILE *f)
242{
Phil Sutter541f1b32015-09-25 14:09:49 +0200243 print_explain(lu, f);
vadimk561e6502014-09-30 08:17:31 +0300244}
245
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800246struct link_util macvlan_link_util = {
247 .id = "macvlan",
248 .maxattr = IFLA_MACVLAN_MAX,
249 .parse_opt = macvlan_parse_opt,
250 .print_opt = macvlan_print_opt,
vadimk561e6502014-09-30 08:17:31 +0300251 .print_help = macvlan_print_help,
Arnd Bergmannd63a9b22009-12-26 11:22:57 -0800252};
Phil Sutter541f1b32015-09-25 14:09:49 +0200253
254struct link_util macvtap_link_util = {
255 .id = "macvtap",
256 .maxattr = IFLA_MACVLAN_MAX,
257 .parse_opt = macvlan_parse_opt,
258 .print_opt = macvlan_print_opt,
259 .print_help = macvlan_print_help,
260};