]>
Commit | Line | Data |
---|---|---|
908755dc JL |
1 | /* |
2 | * iplink_geneve.c GENEVE 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: John W. Linville <linville@tuxdriver.com> | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | ||
f4739b2e | 14 | #include "rt_names.h" |
908755dc JL |
15 | #include "utils.h" |
16 | #include "ip_common.h" | |
17 | ||
c2a85c3b GM |
18 | #define GENEVE_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0) |
19 | ||
908755dc JL |
20 | static void print_explain(FILE *f) |
21 | { | |
8b471354 PS |
22 | fprintf(f, |
23 | "Usage: ... geneve id VNI\n" | |
24 | " remote ADDR\n" | |
25 | " [ ttl TTL ]\n" | |
26 | " [ tos TOS ]\n" | |
64dbd03e | 27 | " [ df DF ]\n" |
8b471354 PS |
28 | " [ flowlabel LABEL ]\n" |
29 | " [ dstport PORT ]\n" | |
30 | " [ [no]external ]\n" | |
31 | " [ [no]udpcsum ]\n" | |
32 | " [ [no]udp6zerocsumtx ]\n" | |
33 | " [ [no]udp6zerocsumrx ]\n" | |
34 | "\n" | |
35 | "Where: VNI := 0-16777215\n" | |
36 | " ADDR := IP_ADDRESS\n" | |
37 | " TOS := { NUMBER | inherit }\n" | |
35b857f9 | 38 | " TTL := { 1..255 | auto | inherit }\n" |
64dbd03e | 39 | " DF := { unset | set | inherit }\n" |
8b471354 PS |
40 | " LABEL := 0-1048575\n" |
41 | ); | |
908755dc JL |
42 | } |
43 | ||
44 | static void explain(void) | |
45 | { | |
46 | print_explain(stderr); | |
47 | } | |
48 | ||
c2a85c3b GM |
49 | static void check_duparg(__u64 *attrs, int type, const char *key, |
50 | const char *argv) | |
51 | { | |
52 | if (!GENEVE_ATTRSET(*attrs, type)) { | |
53 | *attrs |= (1L << type); | |
54 | return; | |
55 | } | |
56 | duparg2(key, argv); | |
57 | } | |
58 | ||
908755dc JL |
59 | static int geneve_parse_opt(struct link_util *lu, int argc, char **argv, |
60 | struct nlmsghdr *n) | |
61 | { | |
6c4b6727 | 62 | inet_prefix daddr; |
908755dc | 63 | __u32 vni = 0; |
29bb2373 | 64 | __u32 label = 0; |
f4c05c2e | 65 | __u8 ttl = 0; |
f4739b2e | 66 | __u8 tos = 0; |
9450c5ec PA |
67 | __u16 dstport = 0; |
68 | bool metadata = 0; | |
325d02b4 | 69 | __u8 udpcsum = 0; |
325d02b4 | 70 | __u8 udp6zerocsumtx = 0; |
325d02b4 | 71 | __u8 udp6zerocsumrx = 0; |
c2a85c3b GM |
72 | __u64 attrs = 0; |
73 | bool set_op = (n->nlmsg_type == RTM_NEWLINK && | |
74 | !(n->nlmsg_flags & NLM_F_CREATE)); | |
908755dc | 75 | |
9cc173d4 | 76 | inet_prefix_reset(&daddr); |
6c4b6727 | 77 | |
908755dc JL |
78 | while (argc > 0) { |
79 | if (!matches(*argv, "id") || | |
80 | !matches(*argv, "vni")) { | |
81 | NEXT_ARG(); | |
c2a85c3b | 82 | check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv); |
908755dc JL |
83 | if (get_u32(&vni, *argv, 0) || |
84 | vni >= 1u << 24) | |
85 | invarg("invalid id", *argv); | |
908755dc JL |
86 | } else if (!matches(*argv, "remote")) { |
87 | NEXT_ARG(); | |
c2a85c3b GM |
88 | check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote", |
89 | *argv); | |
6c4b6727 SP |
90 | get_addr(&daddr, *argv, AF_UNSPEC); |
91 | if (!is_addrtype_inet_not_multi(&daddr)) | |
908755dc | 92 | invarg("invalid remote address", *argv); |
f4c05c2e JL |
93 | } else if (!matches(*argv, "ttl") || |
94 | !matches(*argv, "hoplimit")) { | |
56f5daac | 95 | unsigned int uval; |
f4c05c2e JL |
96 | |
97 | NEXT_ARG(); | |
c2a85c3b | 98 | check_duparg(&attrs, IFLA_GENEVE_TTL, "ttl", *argv); |
35b857f9 HL |
99 | if (strcmp(*argv, "inherit") == 0) { |
100 | addattr8(n, 1024, IFLA_GENEVE_TTL_INHERIT, 1); | |
101 | } else if (strcmp(*argv, "auto") != 0) { | |
f4c05c2e JL |
102 | if (get_unsigned(&uval, *argv, 0)) |
103 | invarg("invalid TTL", *argv); | |
104 | if (uval > 255) | |
105 | invarg("TTL must be <= 255", *argv); | |
106 | ttl = uval; | |
107 | } | |
f4739b2e JL |
108 | } else if (!matches(*argv, "tos") || |
109 | !matches(*argv, "dsfield")) { | |
110 | __u32 uval; | |
111 | ||
112 | NEXT_ARG(); | |
c2a85c3b | 113 | check_duparg(&attrs, IFLA_GENEVE_TOS, "tos", *argv); |
f4739b2e JL |
114 | if (strcmp(*argv, "inherit") != 0) { |
115 | if (rtnl_dsfield_a2n(&uval, *argv)) | |
116 | invarg("bad TOS value", *argv); | |
117 | tos = uval; | |
118 | } else | |
119 | tos = 1; | |
64dbd03e SB |
120 | } else if (!matches(*argv, "df")) { |
121 | enum ifla_geneve_df df; | |
122 | ||
123 | NEXT_ARG(); | |
124 | check_duparg(&attrs, IFLA_GENEVE_DF, "df", *argv); | |
125 | if (strcmp(*argv, "unset") == 0) | |
126 | df = GENEVE_DF_UNSET; | |
127 | else if (strcmp(*argv, "set") == 0) | |
128 | df = GENEVE_DF_SET; | |
129 | else if (strcmp(*argv, "inherit") == 0) | |
130 | df = GENEVE_DF_INHERIT; | |
131 | else | |
132 | invarg("DF must be 'unset', 'set' or 'inherit'", | |
133 | *argv); | |
134 | ||
135 | addattr8(n, 1024, IFLA_GENEVE_DF, df); | |
29bb2373 DB |
136 | } else if (!matches(*argv, "label") || |
137 | !matches(*argv, "flowlabel")) { | |
138 | __u32 uval; | |
139 | ||
140 | NEXT_ARG(); | |
c2a85c3b GM |
141 | check_duparg(&attrs, IFLA_GENEVE_LABEL, "flowlabel", |
142 | *argv); | |
29bb2373 DB |
143 | if (get_u32(&uval, *argv, 0) || |
144 | (uval & ~LABEL_MAX_MASK)) | |
145 | invarg("invalid flowlabel", *argv); | |
146 | label = htonl(uval); | |
9450c5ec PA |
147 | } else if (!matches(*argv, "dstport")) { |
148 | NEXT_ARG(); | |
c2a85c3b GM |
149 | check_duparg(&attrs, IFLA_GENEVE_PORT, "dstport", |
150 | *argv); | |
9450c5ec PA |
151 | if (get_u16(&dstport, *argv, 0)) |
152 | invarg("dstport", *argv); | |
153 | } else if (!matches(*argv, "external")) { | |
c2a85c3b GM |
154 | check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA, |
155 | *argv, *argv); | |
9450c5ec PA |
156 | metadata = true; |
157 | } else if (!matches(*argv, "noexternal")) { | |
c2a85c3b GM |
158 | check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA, |
159 | *argv, *argv); | |
9450c5ec | 160 | metadata = false; |
325d02b4 | 161 | } else if (!matches(*argv, "udpcsum")) { |
c2a85c3b GM |
162 | check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv, |
163 | *argv); | |
325d02b4 | 164 | udpcsum = 1; |
325d02b4 | 165 | } else if (!matches(*argv, "noudpcsum")) { |
c2a85c3b GM |
166 | check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv, |
167 | *argv); | |
325d02b4 | 168 | udpcsum = 0; |
325d02b4 | 169 | } else if (!matches(*argv, "udp6zerocsumtx")) { |
c2a85c3b GM |
170 | check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, |
171 | *argv, *argv); | |
325d02b4 | 172 | udp6zerocsumtx = 1; |
325d02b4 | 173 | } else if (!matches(*argv, "noudp6zerocsumtx")) { |
c2a85c3b GM |
174 | check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, |
175 | *argv, *argv); | |
325d02b4 | 176 | udp6zerocsumtx = 0; |
325d02b4 | 177 | } else if (!matches(*argv, "udp6zerocsumrx")) { |
c2a85c3b GM |
178 | check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, |
179 | *argv, *argv); | |
325d02b4 | 180 | udp6zerocsumrx = 1; |
325d02b4 | 181 | } else if (!matches(*argv, "noudp6zerocsumrx")) { |
c2a85c3b GM |
182 | check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, |
183 | *argv, *argv); | |
325d02b4 | 184 | udp6zerocsumrx = 0; |
908755dc JL |
185 | } else if (matches(*argv, "help") == 0) { |
186 | explain(); | |
187 | return -1; | |
188 | } else { | |
189 | fprintf(stderr, "geneve: unknown command \"%s\"?\n", *argv); | |
190 | explain(); | |
191 | return -1; | |
192 | } | |
193 | argc--, argv++; | |
194 | } | |
195 | ||
c2a85c3b | 196 | if (metadata && GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) { |
9450c5ec | 197 | fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n"); |
908755dc JL |
198 | return -1; |
199 | } | |
200 | ||
9450c5ec PA |
201 | if (!metadata) { |
202 | /* parameter checking make sense only for full geneve tunnels */ | |
c2a85c3b | 203 | if (!GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) { |
9450c5ec PA |
204 | fprintf(stderr, "geneve: missing virtual network identifier\n"); |
205 | return -1; | |
206 | } | |
207 | ||
c2a85c3b GM |
208 | /* If we are modifying the geneve device, then we only need the |
209 | * ID (VNI) to identify the geneve device, and we do not need | |
210 | * the remote IP. | |
211 | */ | |
6c4b6727 | 212 | if (!set_op && !is_addrtype_inet(&daddr)) { |
9450c5ec PA |
213 | fprintf(stderr, "geneve: remote link partner not specified\n"); |
214 | return -1; | |
215 | } | |
908755dc JL |
216 | } |
217 | ||
218 | addattr32(n, 1024, IFLA_GENEVE_ID, vni); | |
6c4b6727 SP |
219 | if (is_addrtype_inet(&daddr)) { |
220 | int type = (daddr.family == AF_INET) ? IFLA_GENEVE_REMOTE : | |
221 | IFLA_GENEVE_REMOTE6; | |
f5393225 | 222 | addattr_l(n, 1024, type, daddr.data, daddr.bytelen); |
c2a85c3b GM |
223 | } |
224 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL)) | |
225 | addattr32(n, 1024, IFLA_GENEVE_LABEL, label); | |
226 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL)) | |
227 | addattr8(n, 1024, IFLA_GENEVE_TTL, ttl); | |
228 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS)) | |
229 | addattr8(n, 1024, IFLA_GENEVE_TOS, tos); | |
9450c5ec PA |
230 | if (dstport) |
231 | addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport)); | |
232 | if (metadata) | |
233 | addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA); | |
c2a85c3b | 234 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM)) |
325d02b4 | 235 | addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum); |
c2a85c3b | 236 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX)) |
325d02b4 | 237 | addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); |
c2a85c3b | 238 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX)) |
325d02b4 | 239 | addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); |
908755dc JL |
240 | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) | |
245 | { | |
246 | __u32 vni; | |
375560c4 | 247 | __u8 ttl = 0; |
3caa526c | 248 | __u8 tos = 0; |
908755dc JL |
249 | |
250 | if (!tb) | |
251 | return; | |
252 | ||
00ff4b8e SP |
253 | if (tb[IFLA_GENEVE_COLLECT_METADATA]) { |
254 | print_bool(PRINT_ANY, "external", "external", true); | |
255 | return; | |
256 | } | |
257 | ||
908755dc JL |
258 | if (!tb[IFLA_GENEVE_ID] || |
259 | RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32)) | |
260 | return; | |
261 | ||
262 | vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]); | |
119fae0a | 263 | print_uint(PRINT_ANY, "id", "id %u ", vni); |
908755dc JL |
264 | |
265 | if (tb[IFLA_GENEVE_REMOTE]) { | |
266 | __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); | |
56f5daac | 267 | |
908755dc | 268 | if (addr) |
119fae0a JF |
269 | print_string(PRINT_ANY, |
270 | "remote", | |
271 | "remote %s ", | |
272 | format_host(AF_INET, 4, &addr)); | |
906ac543 JL |
273 | } else if (tb[IFLA_GENEVE_REMOTE6]) { |
274 | struct in6_addr addr; | |
56f5daac | 275 | |
906ac543 | 276 | memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); |
1f4c51c0 | 277 | if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { |
2642b6b0 | 278 | if (!IN6_IS_ADDR_MULTICAST(&addr)) |
119fae0a JF |
279 | print_string(PRINT_ANY, |
280 | "remote6", | |
281 | "remote %s ", | |
282 | format_host(AF_INET6, | |
283 | sizeof(struct in6_addr), | |
284 | &addr)); | |
906ac543 | 285 | } |
908755dc | 286 | } |
f4c05c2e | 287 | |
35b857f9 HL |
288 | if (tb[IFLA_GENEVE_TTL_INHERIT] && |
289 | rta_getattr_u8(tb[IFLA_GENEVE_TTL_INHERIT])) { | |
375560c4 | 290 | print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); |
35b857f9 HL |
291 | } else if (tb[IFLA_GENEVE_TTL]) { |
292 | ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); | |
293 | if (is_json_context() || ttl) | |
294 | print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); | |
295 | else | |
296 | print_string(PRINT_FP, NULL, "ttl %s ", "auto"); | |
297 | } | |
f4739b2e | 298 | |
3caa526c SP |
299 | if (tb[IFLA_GENEVE_TOS]) |
300 | tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]); | |
301 | if (tos) { | |
302 | if (is_json_context() || tos != 1) | |
90c5c969 | 303 | print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); |
3caa526c SP |
304 | else |
305 | print_string(PRINT_FP, NULL, "tos %s ", "inherit"); | |
f4739b2e | 306 | } |
9450c5ec | 307 | |
64dbd03e SB |
308 | if (tb[IFLA_GENEVE_DF]) { |
309 | enum ifla_geneve_df df = rta_getattr_u8(tb[IFLA_GENEVE_DF]); | |
310 | ||
311 | if (df == GENEVE_DF_UNSET) | |
312 | print_string(PRINT_JSON, "df", "df %s ", "unset"); | |
313 | else if (df == GENEVE_DF_SET) | |
314 | print_string(PRINT_ANY, "df", "df %s ", "set"); | |
315 | else if (df == GENEVE_DF_INHERIT) | |
316 | print_string(PRINT_ANY, "df", "df %s ", "inherit"); | |
317 | } | |
318 | ||
29bb2373 DB |
319 | if (tb[IFLA_GENEVE_LABEL]) { |
320 | __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); | |
321 | ||
322 | if (label) | |
119fae0a | 323 | print_0xhex(PRINT_ANY, |
90c5c969 | 324 | "label", "flowlabel %#llx ", |
119fae0a | 325 | ntohl(label)); |
29bb2373 DB |
326 | } |
327 | ||
9450c5ec | 328 | if (tb[IFLA_GENEVE_PORT]) |
119fae0a JF |
329 | print_uint(PRINT_ANY, |
330 | "port", | |
331 | "dstport %u ", | |
332 | rta_getattr_be16(tb[IFLA_GENEVE_PORT])); | |
9450c5ec | 333 | |
325d02b4 | 334 | if (tb[IFLA_GENEVE_UDP_CSUM]) { |
119fae0a JF |
335 | if (is_json_context()) { |
336 | print_bool(PRINT_JSON, | |
337 | "udp_csum", | |
338 | NULL, | |
339 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])); | |
340 | } else { | |
341 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) | |
342 | fputs("no", f); | |
343 | fputs("udpcsum ", f); | |
344 | } | |
325d02b4 JG |
345 | } |
346 | ||
347 | if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { | |
119fae0a JF |
348 | if (is_json_context()) { |
349 | print_bool(PRINT_JSON, | |
350 | "udp_zero_csum6_tx", | |
351 | NULL, | |
352 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])); | |
353 | } else { | |
354 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) | |
355 | fputs("no", f); | |
356 | fputs("udp6zerocsumtx ", f); | |
357 | } | |
325d02b4 JG |
358 | } |
359 | ||
360 | if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { | |
119fae0a JF |
361 | if (is_json_context()) { |
362 | print_bool(PRINT_JSON, | |
363 | "udp_zero_csum6_rx", | |
364 | NULL, | |
365 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])); | |
366 | } else { | |
367 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) | |
368 | fputs("no", f); | |
369 | fputs("udp6zerocsumrx ", f); | |
370 | } | |
325d02b4 | 371 | } |
908755dc JL |
372 | } |
373 | ||
374 | static void geneve_print_help(struct link_util *lu, int argc, char **argv, | |
375 | FILE *f) | |
376 | { | |
377 | print_explain(f); | |
378 | } | |
379 | ||
380 | struct link_util geneve_link_util = { | |
381 | .id = "geneve", | |
382 | .maxattr = IFLA_GENEVE_MAX, | |
383 | .parse_opt = geneve_parse_opt, | |
384 | .print_opt = geneve_print_opt, | |
385 | .print_help = geneve_print_help, | |
386 | }; |