]>
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" | |
37 | " TTL := { 1..255 | inherit }\n" | |
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 | { | |
60 | __u32 vni = 0; | |
908755dc JL |
61 | __u32 daddr = 0; |
62 | struct in6_addr daddr6 = IN6ADDR_ANY_INIT; | |
29bb2373 | 63 | __u32 label = 0; |
f4c05c2e | 64 | __u8 ttl = 0; |
f4739b2e | 65 | __u8 tos = 0; |
9450c5ec PA |
66 | __u16 dstport = 0; |
67 | bool metadata = 0; | |
325d02b4 | 68 | __u8 udpcsum = 0; |
325d02b4 | 69 | __u8 udp6zerocsumtx = 0; |
325d02b4 | 70 | __u8 udp6zerocsumrx = 0; |
c2a85c3b GM |
71 | __u64 attrs = 0; |
72 | bool set_op = (n->nlmsg_type == RTM_NEWLINK && | |
73 | !(n->nlmsg_flags & NLM_F_CREATE)); | |
908755dc JL |
74 | |
75 | while (argc > 0) { | |
76 | if (!matches(*argv, "id") || | |
77 | !matches(*argv, "vni")) { | |
78 | NEXT_ARG(); | |
c2a85c3b | 79 | check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv); |
908755dc JL |
80 | if (get_u32(&vni, *argv, 0) || |
81 | vni >= 1u << 24) | |
82 | invarg("invalid id", *argv); | |
908755dc JL |
83 | } else if (!matches(*argv, "remote")) { |
84 | NEXT_ARG(); | |
c2a85c3b GM |
85 | check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote", |
86 | *argv); | |
908755dc JL |
87 | if (!inet_get_addr(*argv, &daddr, &daddr6)) { |
88 | fprintf(stderr, "Invalid address \"%s\"\n", *argv); | |
89 | return -1; | |
90 | } | |
906ac543 | 91 | if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(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); |
f4c05c2e JL |
99 | if (strcmp(*argv, "inherit") != 0) { |
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 | */ | |
194 | if (!set_op && !daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { | |
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); | |
201 | if (daddr) | |
202 | addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4); | |
c2a85c3b GM |
203 | if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { |
204 | addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, | |
205 | sizeof(struct in6_addr)); | |
206 | } | |
207 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL)) | |
208 | addattr32(n, 1024, IFLA_GENEVE_LABEL, label); | |
209 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL)) | |
210 | addattr8(n, 1024, IFLA_GENEVE_TTL, ttl); | |
211 | if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS)) | |
212 | addattr8(n, 1024, IFLA_GENEVE_TOS, tos); | |
9450c5ec PA |
213 | if (dstport) |
214 | addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport)); | |
215 | if (metadata) | |
216 | addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA); | |
c2a85c3b | 217 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM)) |
325d02b4 | 218 | addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum); |
c2a85c3b | 219 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX)) |
325d02b4 | 220 | addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); |
c2a85c3b | 221 | if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX)) |
325d02b4 | 222 | addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); |
908755dc JL |
223 | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) | |
228 | { | |
229 | __u32 vni; | |
375560c4 | 230 | __u8 ttl = 0; |
f4739b2e | 231 | __u8 tos; |
908755dc JL |
232 | |
233 | if (!tb) | |
234 | return; | |
235 | ||
236 | if (!tb[IFLA_GENEVE_ID] || | |
237 | RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32)) | |
238 | return; | |
239 | ||
240 | vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]); | |
119fae0a | 241 | print_uint(PRINT_ANY, "id", "id %u ", vni); |
908755dc JL |
242 | |
243 | if (tb[IFLA_GENEVE_REMOTE]) { | |
244 | __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); | |
56f5daac | 245 | |
908755dc | 246 | if (addr) |
119fae0a JF |
247 | print_string(PRINT_ANY, |
248 | "remote", | |
249 | "remote %s ", | |
250 | format_host(AF_INET, 4, &addr)); | |
906ac543 JL |
251 | } else if (tb[IFLA_GENEVE_REMOTE6]) { |
252 | struct in6_addr addr; | |
56f5daac | 253 | |
906ac543 | 254 | memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); |
1f4c51c0 | 255 | if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { |
2642b6b0 | 256 | if (!IN6_IS_ADDR_MULTICAST(&addr)) |
119fae0a JF |
257 | print_string(PRINT_ANY, |
258 | "remote6", | |
259 | "remote %s ", | |
260 | format_host(AF_INET6, | |
261 | sizeof(struct in6_addr), | |
262 | &addr)); | |
906ac543 | 263 | } |
908755dc | 264 | } |
f4c05c2e | 265 | |
375560c4 SP |
266 | if (tb[IFLA_GENEVE_TTL]) |
267 | ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); | |
268 | if (is_json_context() || ttl) | |
269 | print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); | |
270 | else | |
271 | print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); | |
f4739b2e JL |
272 | |
273 | if (tb[IFLA_GENEVE_TOS] && | |
274 | (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) { | |
119fae0a JF |
275 | if (is_json_context()) { |
276 | print_0xhex(PRINT_JSON, "tos", "%#x", tos); | |
277 | } else { | |
278 | if (tos == 1) { | |
279 | print_string(PRINT_FP, | |
280 | "tos", | |
281 | "tos %s ", | |
282 | "inherit"); | |
283 | } else { | |
284 | fprintf(f, "tos %#x ", tos); | |
285 | } | |
286 | } | |
f4739b2e | 287 | } |
9450c5ec | 288 | |
29bb2373 DB |
289 | if (tb[IFLA_GENEVE_LABEL]) { |
290 | __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); | |
291 | ||
292 | if (label) | |
119fae0a JF |
293 | print_0xhex(PRINT_ANY, |
294 | "label", | |
295 | "flowlabel %#x ", | |
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 PA |
304 | |
305 | if (tb[IFLA_GENEVE_COLLECT_METADATA]) | |
119fae0a | 306 | print_bool(PRINT_ANY, "collect_metadata", "external ", true); |
9450c5ec | 307 | |
325d02b4 | 308 | if (tb[IFLA_GENEVE_UDP_CSUM]) { |
119fae0a JF |
309 | if (is_json_context()) { |
310 | print_bool(PRINT_JSON, | |
311 | "udp_csum", | |
312 | NULL, | |
313 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])); | |
314 | } else { | |
315 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) | |
316 | fputs("no", f); | |
317 | fputs("udpcsum ", f); | |
318 | } | |
325d02b4 JG |
319 | } |
320 | ||
321 | if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { | |
119fae0a JF |
322 | if (is_json_context()) { |
323 | print_bool(PRINT_JSON, | |
324 | "udp_zero_csum6_tx", | |
325 | NULL, | |
326 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])); | |
327 | } else { | |
328 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) | |
329 | fputs("no", f); | |
330 | fputs("udp6zerocsumtx ", f); | |
331 | } | |
325d02b4 JG |
332 | } |
333 | ||
334 | if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { | |
119fae0a JF |
335 | if (is_json_context()) { |
336 | print_bool(PRINT_JSON, | |
337 | "udp_zero_csum6_rx", | |
338 | NULL, | |
339 | rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])); | |
340 | } else { | |
341 | if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) | |
342 | fputs("no", f); | |
343 | fputs("udp6zerocsumrx ", f); | |
344 | } | |
325d02b4 | 345 | } |
908755dc JL |
346 | } |
347 | ||
348 | static void geneve_print_help(struct link_util *lu, int argc, char **argv, | |
349 | FILE *f) | |
350 | { | |
351 | print_explain(f); | |
352 | } | |
353 | ||
354 | struct link_util geneve_link_util = { | |
355 | .id = "geneve", | |
356 | .maxattr = IFLA_GENEVE_MAX, | |
357 | .parse_opt = geneve_parse_opt, | |
358 | .print_opt = geneve_print_opt, | |
359 | .print_help = geneve_print_help, | |
360 | }; |