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