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