blob: 53c50accde893d973cb1483ab34fd5fc840b1975 [file] [log] [blame]
Stephen Hemmingera5494df2012-09-24 12:48:29 -07001/*
2 * iplink_vxlan.c VXLAN 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: Stephen Hemminger <shemminger@vyatta.com
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <net/if.h>
16#include <linux/ip.h>
17#include <linux/if_link.h>
18#include <arpa/inet.h>
19
20#include "rt_names.h"
21#include "utils.h"
22#include "ip_common.h"
23
24static void explain(void)
25{
Atzm Watanabe7cfa3802013-07-24 14:01:01 +090026 fprintf(stderr, "Usage: ... vxlan id VNI [ { group | remote } ADDR ] [ local ADDR ]\n");
Stephen Hemminger2d596122012-10-09 23:39:17 -070027 fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n");
Stephen Hemminger514cdfb2014-01-10 15:17:06 -080028 fprintf(stderr, " [ dstport PORT ] [ srcport MIN MAX ]\n");
29 fprintf(stderr, " [ [no]learning ] [ [no]proxy ] [ [no]rsc ]\n");
David L Stevens1556e292012-12-12 10:02:19 -080030 fprintf(stderr, " [ [no]l2miss ] [ [no]l3miss ]\n");
Stephen Hemmingera5494df2012-09-24 12:48:29 -070031 fprintf(stderr, "\n");
32 fprintf(stderr, "Where: VNI := 0-16777215\n");
33 fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n");
34 fprintf(stderr, " TOS := { NUMBER | inherit }\n");
35 fprintf(stderr, " TTL := { 1..255 | inherit }\n");
36}
37
38static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
39 struct nlmsghdr *n)
40{
41 __u32 vni = 0;
42 int vni_set = 0;
43 __u32 saddr = 0;
44 __u32 gaddr = 0;
Atzm Watanabe7cfa3802013-07-24 14:01:01 +090045 __u32 daddr = 0;
WANG Congaa574cd2013-10-16 22:03:48 -070046 struct in6_addr saddr6 = IN6ADDR_ANY_INIT;
47 struct in6_addr gaddr6 = IN6ADDR_ANY_INIT;
48 struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
Stephen Hemmingera5494df2012-09-24 12:48:29 -070049 unsigned link = 0;
50 __u8 tos = 0;
51 __u8 ttl = 0;
52 __u8 learning = 1;
David L Stevens1556e292012-12-12 10:02:19 -080053 __u8 proxy = 0;
54 __u8 rsc = 0;
55 __u8 l2miss = 0;
56 __u8 l3miss = 0;
Stephen Hemmingera5494df2012-09-24 12:48:29 -070057 __u8 noage = 0;
58 __u32 age = 0;
59 __u32 maxaddr = 0;
Stephen Hemminger514cdfb2014-01-10 15:17:06 -080060 __u16 dstport = 0;
61 int dst_port_set = 0;
Stephen Hemminger2d596122012-10-09 23:39:17 -070062 struct ifla_vxlan_port_range range = { 0, 0 };
Stephen Hemmingera5494df2012-09-24 12:48:29 -070063
64 while (argc > 0) {
65 if (!matches(*argv, "id") ||
66 !matches(*argv, "vni")) {
67 NEXT_ARG();
68 if (get_u32(&vni, *argv, 0) ||
69 vni >= 1u << 24)
70 invarg("invalid id", *argv);
71 vni_set = 1;
72 } else if (!matches(*argv, "group")) {
73 NEXT_ARG();
WANG Congaa574cd2013-10-16 22:03:48 -070074 if (!inet_get_addr(*argv, &gaddr, &gaddr6)) {
75 fprintf(stderr, "Invalid address \"%s\"\n", *argv);
76 return -1;
77 }
78 if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr)))
Atzm Watanabe7cfa3802013-07-24 14:01:01 +090079 invarg("invalid group address", *argv);
80 } else if (!matches(*argv, "remote")) {
81 NEXT_ARG();
WANG Congaa574cd2013-10-16 22:03:48 -070082 if (!inet_get_addr(*argv, &daddr, &daddr6)) {
83 fprintf(stderr, "Invalid address \"%s\"\n", *argv);
84 return -1;
85 }
86 if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
Atzm Watanabe7cfa3802013-07-24 14:01:01 +090087 invarg("invalid remote address", *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -070088 } else if (!matches(*argv, "local")) {
89 NEXT_ARG();
WANG Congaa574cd2013-10-16 22:03:48 -070090 if (strcmp(*argv, "any")) {
91 if (!inet_get_addr(*argv, &saddr, &saddr6)) {
92 fprintf(stderr, "Invalid address \"%s\"\n", *argv);
93 return -1;
94 }
95 }
96
97 if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6))
Stephen Hemmingera5494df2012-09-24 12:48:29 -070098 invarg("invalid local address", *argv);
99 } else if (!matches(*argv, "dev")) {
100 NEXT_ARG();
101 link = if_nametoindex(*argv);
Cong Wang0cb6bb52014-06-03 16:06:45 -0700102 if (link == 0) {
103 fprintf(stderr, "Cannot find device \"%s\"\n",
104 *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700105 exit(-1);
Cong Wang0cb6bb52014-06-03 16:06:45 -0700106 }
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700107 } else if (!matches(*argv, "ttl") ||
108 !matches(*argv, "hoplimit")) {
109 unsigned uval;
110
111 NEXT_ARG();
112 if (strcmp(*argv, "inherit") != 0) {
113 if (get_unsigned(&uval, *argv, 0))
Stephen Hemminger2d596122012-10-09 23:39:17 -0700114 invarg("invalid TTL", *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700115 if (uval > 255)
Stephen Hemminger2d596122012-10-09 23:39:17 -0700116 invarg("TTL must be <= 255", *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700117 ttl = uval;
118 }
119 } else if (!matches(*argv, "tos") ||
120 !matches(*argv, "dsfield")) {
121 __u32 uval;
122
123 NEXT_ARG();
124 if (strcmp(*argv, "inherit") != 0) {
125 if (rtnl_dsfield_a2n(&uval, *argv))
126 invarg("bad TOS value", *argv);
127 tos = uval;
128 } else
129 tos = 1;
130 } else if (!matches(*argv, "ageing")) {
131 NEXT_ARG();
132 if (strcmp(*argv, "none") == 0)
133 noage = 1;
134 else if (get_u32(&age, *argv, 0))
Stephen Hemminger2d596122012-10-09 23:39:17 -0700135 invarg("ageing timer", *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700136 } else if (!matches(*argv, "maxaddress")) {
137 NEXT_ARG();
138 if (strcmp(*argv, "unlimited") == 0)
139 maxaddr = 0;
140 else if (get_u32(&maxaddr, *argv, 0))
Stephen Hemminger2d596122012-10-09 23:39:17 -0700141 invarg("max addresses", *argv);
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800142 } else if (!matches(*argv, "port") ||
143 !matches(*argv, "srcport")) {
Stephen Hemminger2d596122012-10-09 23:39:17 -0700144 __u16 minport, maxport;
145 NEXT_ARG();
146 if (get_u16(&minport, *argv, 0))
147 invarg("min port", *argv);
148 NEXT_ARG();
149 if (get_u16(&maxport, *argv, 0))
150 invarg("max port", *argv);
151 range.low = htons(minport);
152 range.high = htons(maxport);
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800153 } else if (!matches(*argv, "dstport")){
154 NEXT_ARG();
155 if (get_u16(&dstport, *argv, 0))
156 invarg("dst port", *argv);
157 dst_port_set = 1;
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700158 } else if (!matches(*argv, "nolearning")) {
159 learning = 0;
160 } else if (!matches(*argv, "learning")) {
161 learning = 1;
David L Stevens1556e292012-12-12 10:02:19 -0800162 } else if (!matches(*argv, "noproxy")) {
163 proxy = 0;
164 } else if (!matches(*argv, "proxy")) {
165 proxy = 1;
166 } else if (!matches(*argv, "norsc")) {
167 rsc = 0;
168 } else if (!matches(*argv, "rsc")) {
169 rsc = 1;
170 } else if (!matches(*argv, "nol2miss")) {
171 l2miss = 0;
172 } else if (!matches(*argv, "l2miss")) {
173 l2miss = 1;
174 } else if (!matches(*argv, "nol3miss")) {
175 l3miss = 0;
176 } else if (!matches(*argv, "l3miss")) {
177 l3miss = 1;
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700178 } else if (matches(*argv, "help") == 0) {
179 explain();
180 return -1;
181 } else {
Kees van Reeuwijk14645ec2013-02-08 03:32:36 +0000182 fprintf(stderr, "vxlan: unknown command \"%s\"?\n", *argv);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700183 explain();
184 return -1;
185 }
186 argc--, argv++;
187 }
Stephen Hemminger2a126a82013-05-15 13:41:49 -0700188
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700189 if (!vni_set) {
190 fprintf(stderr, "vxlan: missing virtual network identifier\n");
191 return -1;
192 }
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800193
WANG Congaa574cd2013-10-16 22:03:48 -0700194 if ((gaddr && daddr) ||
195 (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) &&
196 memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) {
Atzm Watanabe7cfa3802013-07-24 14:01:01 +0900197 fprintf(stderr, "vxlan: both group and remote cannot be specified\n");
198 return -1;
199 }
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800200
201 if (!dst_port_set) {
202 fprintf(stderr, "vxlan: destination port not specified\n"
203 "Will use Linux kernel default (non-standard value)\n");
Stephen Hemminger06125192014-02-17 10:55:31 -0800204 fprintf(stderr,
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800205 "Use 'dstport 4789' to get the IANA assigned value\n"
206 "Use 'dstport 0' to get default and quiet this message\n");
207 }
208
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700209 addattr32(n, 1024, IFLA_VXLAN_ID, vni);
Stephen Hemmingerb64da5a2012-10-19 13:25:17 -0700210 if (gaddr)
211 addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
Atzm Watanabe7cfa3802013-07-24 14:01:01 +0900212 else if (daddr)
213 addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
WANG Congaa574cd2013-10-16 22:03:48 -0700214 if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0)
215 addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr));
216 else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
217 addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
218
Stephen Hemmingerb64da5a2012-10-19 13:25:17 -0700219 if (saddr)
220 addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
WANG Congaa574cd2013-10-16 22:03:48 -0700221 else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0)
222 addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr));
223
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700224 if (link)
225 addattr32(n, 1024, IFLA_VXLAN_LINK, link);
226 addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
227 addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
228 addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
David L Stevens1556e292012-12-12 10:02:19 -0800229 addattr8(n, 1024, IFLA_VXLAN_PROXY, proxy);
230 addattr8(n, 1024, IFLA_VXLAN_RSC, rsc);
231 addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss);
232 addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss);
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800233
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700234 if (noage)
235 addattr32(n, 1024, IFLA_VXLAN_AGEING, 0);
236 else if (age)
237 addattr32(n, 1024, IFLA_VXLAN_AGEING, age);
238 if (maxaddr)
239 addattr32(n, 1024, IFLA_VXLAN_LIMIT, maxaddr);
Stephen Hemminger2d596122012-10-09 23:39:17 -0700240 if (range.low || range.high)
241 addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE,
242 &range, sizeof(range));
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800243 if (dstport)
244 addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport));
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700245
246 return 0;
247}
248
249static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
250{
251 __u32 vni;
252 unsigned link;
Stephen Hemminger2d596122012-10-09 23:39:17 -0700253 __u8 tos;
254 __u32 maxaddr;
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700255 char s1[1024];
256 char s2[64];
257
258 if (!tb)
259 return;
260
261 if (!tb[IFLA_VXLAN_ID] ||
262 RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) < sizeof(__u32))
263 return;
264
265 vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]);
266 fprintf(f, "id %u ", vni);
267
268 if (tb[IFLA_VXLAN_GROUP]) {
Stephen Hemmingerb64da5a2012-10-19 13:25:17 -0700269 __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]);
Atzm Watanabe7cfa3802013-07-24 14:01:01 +0900270 if (addr) {
271 if (IN_MULTICAST(ntohl(addr)))
272 fprintf(f, "group %s ",
273 format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
274 else
275 fprintf(f, "remote %s ",
276 format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
277 }
WANG Congaa574cd2013-10-16 22:03:48 -0700278 } else if (tb[IFLA_VXLAN_GROUP6]) {
279 struct in6_addr addr;
280 memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr));
281 if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
282 if (IN6_IS_ADDR_MULTICAST(&addr))
283 fprintf(f, "group %s ",
284 format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
285 else
286 fprintf(f, "remote %s ",
287 format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
288 }
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700289 }
290
291 if (tb[IFLA_VXLAN_LOCAL]) {
Stephen Hemmingerb64da5a2012-10-19 13:25:17 -0700292 __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700293 if (addr)
David L Stevens1556e292012-12-12 10:02:19 -0800294 fprintf(f, "local %s ",
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700295 format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
WANG Congaa574cd2013-10-16 22:03:48 -0700296 } else if (tb[IFLA_VXLAN_LOCAL6]) {
297 struct in6_addr addr;
298 memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr));
299 if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0)
300 fprintf(f, "local %s ",
301 format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700302 }
303
304 if (tb[IFLA_VXLAN_LINK] &&
305 (link = rta_getattr_u32(tb[IFLA_VXLAN_LINK]))) {
306 const char *n = if_indextoname(link, s2);
307
308 if (n)
309 fprintf(f, "dev %s ", n);
310 else
311 fprintf(f, "dev %u ", link);
312 }
313
Stephen Hemminger2d596122012-10-09 23:39:17 -0700314 if (tb[IFLA_VXLAN_PORT_RANGE]) {
315 const struct ifla_vxlan_port_range *r
316 = RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]);
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800317 fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high));
David L Stevens1556e292012-12-12 10:02:19 -0800318 }
Stephen Hemminger2d596122012-10-09 23:39:17 -0700319
Stephen Hemminger514cdfb2014-01-10 15:17:06 -0800320 if (tb[IFLA_VXLAN_PORT])
321 fprintf(f, "dstport %u ",
322 ntohs(rta_getattr_u16(tb[IFLA_VXLAN_PORT])));
323
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700324 if (tb[IFLA_VXLAN_LEARNING] &&
325 !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
326 fputs("nolearning ", f);
David L Stevens1556e292012-12-12 10:02:19 -0800327
328 if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY]))
329 fputs("proxy ", f);
330
331 if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC]))
332 fputs("rsc ", f);
333
334 if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS]))
335 fputs("l2miss ", f);
336
337 if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS]))
338 fputs("l3miss ", f);
339
Stephen Hemminger2d596122012-10-09 23:39:17 -0700340 if (tb[IFLA_VXLAN_TOS] &&
341 (tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]))) {
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700342 if (tos == 1)
343 fprintf(f, "tos inherit ");
344 else
345 fprintf(f, "tos %#x ", tos);
346 }
347
348 if (tb[IFLA_VXLAN_TTL]) {
349 __u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]);
350 if (ttl)
351 fprintf(f, "ttl %d ", ttl);
352 }
353
354 if (tb[IFLA_VXLAN_AGEING]) {
355 __u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]);
356 if (age == 0)
357 fprintf(f, "ageing none ");
358 else
359 fprintf(f, "ageing %u ", age);
360 }
Stephen Hemminger2d596122012-10-09 23:39:17 -0700361
362 if (tb[IFLA_VXLAN_LIMIT] &&
363 (maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT]) != 0))
364 fprintf(f, "maxaddr %u ", maxaddr);
Stephen Hemmingera5494df2012-09-24 12:48:29 -0700365}
366
367struct link_util vxlan_link_util = {
368 .id = "vxlan",
369 .maxattr = IFLA_VXLAN_MAX,
370 .parse_opt = vxlan_parse_opt,
371 .print_opt = vxlan_print_opt,
372};