]>
Commit | Line | Data |
---|---|---|
1e529305 RP |
1 | /* |
2 | * iproute_lwtunnel.c | |
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: Roopa Prabhu, <roopa@cumulusnetworks.com> | |
56f5daac | 10 | * Thomas Graf <tgraf@suug.ch> |
1e529305 RP |
11 | * |
12 | */ | |
13 | ||
14 | #include <stdio.h> | |
15 | #include <stdlib.h> | |
16 | #include <unistd.h> | |
17 | #include <fcntl.h> | |
18 | #include <string.h> | |
5866bddd | 19 | #include <linux/ila.h> |
1e529305 RP |
20 | #include <linux/lwtunnel.h> |
21 | #include <linux/mpls_iptunnel.h> | |
22 | #include <errno.h> | |
23 | ||
24 | #include "rt_names.h" | |
25 | #include "utils.h" | |
26 | #include "iproute_lwtunnel.h" | |
27 | ||
28 | static int read_encap_type(const char *name) | |
29 | { | |
30 | if (strcmp(name, "mpls") == 0) | |
31 | return LWTUNNEL_ENCAP_MPLS; | |
32 | else if (strcmp(name, "ip") == 0) | |
33 | return LWTUNNEL_ENCAP_IP; | |
34 | else if (strcmp(name, "ip6") == 0) | |
35 | return LWTUNNEL_ENCAP_IP6; | |
5866bddd TH |
36 | else if (strcmp(name, "ila") == 0) |
37 | return LWTUNNEL_ENCAP_ILA; | |
1e529305 RP |
38 | else |
39 | return LWTUNNEL_ENCAP_NONE; | |
40 | } | |
41 | ||
42 | static const char *format_encap_type(int type) | |
43 | { | |
44 | switch (type) { | |
45 | case LWTUNNEL_ENCAP_MPLS: | |
46 | return "mpls"; | |
47 | case LWTUNNEL_ENCAP_IP: | |
48 | return "ip"; | |
49 | case LWTUNNEL_ENCAP_IP6: | |
50 | return "ip6"; | |
5866bddd TH |
51 | case LWTUNNEL_ENCAP_ILA: |
52 | return "ila"; | |
1e529305 RP |
53 | default: |
54 | return "unknown"; | |
55 | } | |
56 | } | |
57 | ||
58 | static void print_encap_mpls(FILE *fp, struct rtattr *encap) | |
59 | { | |
60 | struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; | |
1e529305 RP |
61 | |
62 | parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap); | |
63 | ||
64 | if (tb[MPLS_IPTUNNEL_DST]) | |
d49f934c PS |
65 | fprintf(fp, " %s ", |
66 | format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST])); | |
1e529305 RP |
67 | } |
68 | ||
69 | static void print_encap_ip(FILE *fp, struct rtattr *encap) | |
70 | { | |
71 | struct rtattr *tb[LWTUNNEL_IP_MAX+1]; | |
1e529305 RP |
72 | |
73 | parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap); | |
74 | ||
75 | if (tb[LWTUNNEL_IP_ID]) | |
76 | fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); | |
77 | ||
78 | if (tb[LWTUNNEL_IP_SRC]) | |
79 | fprintf(fp, "src %s ", | |
7faf1588 | 80 | rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); |
1e529305 RP |
81 | |
82 | if (tb[LWTUNNEL_IP_DST]) | |
83 | fprintf(fp, "dst %s ", | |
7faf1588 | 84 | rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); |
1e529305 RP |
85 | |
86 | if (tb[LWTUNNEL_IP_TTL]) | |
87 | fprintf(fp, "ttl %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); | |
88 | ||
89 | if (tb[LWTUNNEL_IP_TOS]) | |
90 | fprintf(fp, "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); | |
91 | } | |
92 | ||
5866bddd TH |
93 | static void print_encap_ila(FILE *fp, struct rtattr *encap) |
94 | { | |
95 | struct rtattr *tb[ILA_ATTR_MAX+1]; | |
96 | ||
97 | parse_rtattr_nested(tb, ILA_ATTR_MAX, encap); | |
98 | ||
99 | if (tb[ILA_ATTR_LOCATOR]) { | |
100 | char abuf[ADDR64_BUF_SIZE]; | |
101 | ||
102 | addr64_n2a(*(__u64 *)RTA_DATA(tb[ILA_ATTR_LOCATOR]), | |
103 | abuf, sizeof(abuf)); | |
104 | fprintf(fp, " %s ", abuf); | |
105 | } | |
106 | } | |
107 | ||
d95cdcf5 PA |
108 | static void print_encap_ip6(FILE *fp, struct rtattr *encap) |
109 | { | |
110 | struct rtattr *tb[LWTUNNEL_IP6_MAX+1]; | |
d95cdcf5 PA |
111 | |
112 | parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap); | |
113 | ||
114 | if (tb[LWTUNNEL_IP6_ID]) | |
115 | fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); | |
116 | ||
117 | if (tb[LWTUNNEL_IP6_SRC]) | |
118 | fprintf(fp, "src %s ", | |
7faf1588 | 119 | rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); |
d95cdcf5 PA |
120 | |
121 | if (tb[LWTUNNEL_IP6_DST]) | |
122 | fprintf(fp, "dst %s ", | |
7faf1588 | 123 | rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); |
d95cdcf5 PA |
124 | |
125 | if (tb[LWTUNNEL_IP6_HOPLIMIT]) | |
126 | fprintf(fp, "hoplimit %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); | |
127 | ||
128 | if (tb[LWTUNNEL_IP6_TC]) | |
129 | fprintf(fp, "tc %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); | |
130 | } | |
131 | ||
1e529305 RP |
132 | void lwt_print_encap(FILE *fp, struct rtattr *encap_type, |
133 | struct rtattr *encap) | |
134 | { | |
135 | int et; | |
136 | ||
137 | if (!encap_type) | |
138 | return; | |
139 | ||
140 | et = rta_getattr_u16(encap_type); | |
141 | ||
d95cdcf5 | 142 | fprintf(fp, " encap %s ", format_encap_type(et)); |
1e529305 RP |
143 | |
144 | switch (et) { | |
145 | case LWTUNNEL_ENCAP_MPLS: | |
146 | print_encap_mpls(fp, encap); | |
147 | break; | |
148 | case LWTUNNEL_ENCAP_IP: | |
149 | print_encap_ip(fp, encap); | |
150 | break; | |
5866bddd TH |
151 | case LWTUNNEL_ENCAP_ILA: |
152 | print_encap_ila(fp, encap); | |
153 | break; | |
d95cdcf5 PA |
154 | case LWTUNNEL_ENCAP_IP6: |
155 | print_encap_ip6(fp, encap); | |
156 | break; | |
1e529305 RP |
157 | } |
158 | } | |
159 | ||
160 | static int parse_encap_mpls(struct rtattr *rta, size_t len, int *argcp, char ***argvp) | |
161 | { | |
162 | inet_prefix addr; | |
163 | int argc = *argcp; | |
164 | char **argv = *argvp; | |
165 | ||
166 | if (get_addr(&addr, *argv, AF_MPLS)) { | |
167 | fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", *argv); | |
168 | exit(1); | |
169 | } | |
170 | ||
171 | rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data, | |
172 | addr.bytelen); | |
173 | ||
174 | *argcp = argc; | |
175 | *argvp = argv; | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static int parse_encap_ip(struct rtattr *rta, size_t len, int *argcp, char ***argvp) | |
181 | { | |
182 | int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; | |
183 | char **argv = *argvp; | |
184 | int argc = *argcp; | |
185 | ||
186 | while (argc > 0) { | |
187 | if (strcmp(*argv, "id") == 0) { | |
188 | __u64 id; | |
56f5daac | 189 | |
1e529305 RP |
190 | NEXT_ARG(); |
191 | if (id_ok++) | |
192 | duparg2("id", *argv); | |
193 | if (get_u64(&id, *argv, 0)) | |
194 | invarg("\"id\" value is invalid\n", *argv); | |
195 | rta_addattr64(rta, len, LWTUNNEL_IP_ID, htonll(id)); | |
196 | } else if (strcmp(*argv, "dst") == 0) { | |
197 | inet_prefix addr; | |
56f5daac | 198 | |
1e529305 RP |
199 | NEXT_ARG(); |
200 | if (dst_ok++) | |
201 | duparg2("dst", *argv); | |
202 | get_addr(&addr, *argv, AF_INET); | |
203 | rta_addattr_l(rta, len, LWTUNNEL_IP_DST, &addr.data, addr.bytelen); | |
204 | } else if (strcmp(*argv, "tos") == 0) { | |
205 | __u32 tos; | |
56f5daac | 206 | |
1e529305 RP |
207 | NEXT_ARG(); |
208 | if (tos_ok++) | |
209 | duparg2("tos", *argv); | |
210 | if (rtnl_dsfield_a2n(&tos, *argv)) | |
211 | invarg("\"tos\" value is invalid\n", *argv); | |
212 | rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos); | |
213 | } else if (strcmp(*argv, "ttl") == 0) { | |
214 | __u8 ttl; | |
56f5daac | 215 | |
1e529305 RP |
216 | NEXT_ARG(); |
217 | if (ttl_ok++) | |
218 | duparg2("ttl", *argv); | |
219 | if (get_u8(&ttl, *argv, 0)) | |
220 | invarg("\"ttl\" value is invalid\n", *argv); | |
221 | rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); | |
222 | } else { | |
223 | break; | |
224 | } | |
f0df4081 | 225 | argc--; argv++; |
1e529305 RP |
226 | } |
227 | ||
f0df4081 PA |
228 | /* argv is currently the first unparsed argument, |
229 | * but the lwt_parse_encap() caller will move to the next, | |
230 | * so step back */ | |
231 | *argcp = argc + 1; | |
232 | *argvp = argv - 1; | |
1e529305 RP |
233 | |
234 | return 0; | |
235 | } | |
236 | ||
5866bddd TH |
237 | static int parse_encap_ila(struct rtattr *rta, size_t len, |
238 | int *argcp, char ***argvp) | |
239 | { | |
240 | __u64 locator; | |
241 | int argc = *argcp; | |
242 | char **argv = *argvp; | |
243 | ||
244 | if (get_addr64(&locator, *argv) < 0) { | |
245 | fprintf(stderr, "Bad locator: %s\n", *argv); | |
246 | exit(1); | |
247 | } | |
248 | ||
249 | rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator); | |
250 | ||
251 | *argcp = argc; | |
252 | *argvp = argv; | |
253 | ||
254 | return 0; | |
255 | } | |
1e529305 | 256 | |
d95cdcf5 PA |
257 | static int parse_encap_ip6(struct rtattr *rta, size_t len, int *argcp, char ***argvp) |
258 | { | |
259 | int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; | |
260 | char **argv = *argvp; | |
261 | int argc = *argcp; | |
262 | ||
263 | while (argc > 0) { | |
264 | if (strcmp(*argv, "id") == 0) { | |
265 | __u64 id; | |
56f5daac | 266 | |
d95cdcf5 PA |
267 | NEXT_ARG(); |
268 | if (id_ok++) | |
269 | duparg2("id", *argv); | |
270 | if (get_u64(&id, *argv, 0)) | |
271 | invarg("\"id\" value is invalid\n", *argv); | |
272 | rta_addattr64(rta, len, LWTUNNEL_IP6_ID, htonll(id)); | |
273 | } else if (strcmp(*argv, "dst") == 0) { | |
274 | inet_prefix addr; | |
56f5daac | 275 | |
d95cdcf5 PA |
276 | NEXT_ARG(); |
277 | if (dst_ok++) | |
278 | duparg2("dst", *argv); | |
279 | get_addr(&addr, *argv, AF_INET6); | |
280 | rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, &addr.data, addr.bytelen); | |
281 | } else if (strcmp(*argv, "tc") == 0) { | |
282 | __u32 tc; | |
56f5daac | 283 | |
d95cdcf5 PA |
284 | NEXT_ARG(); |
285 | if (tos_ok++) | |
286 | duparg2("tc", *argv); | |
287 | if (rtnl_dsfield_a2n(&tc, *argv)) | |
288 | invarg("\"tc\" value is invalid\n", *argv); | |
289 | rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc); | |
290 | } else if (strcmp(*argv, "hoplimit") == 0) { | |
291 | __u8 hoplimit; | |
56f5daac | 292 | |
d95cdcf5 PA |
293 | NEXT_ARG(); |
294 | if (ttl_ok++) | |
295 | duparg2("hoplimit", *argv); | |
296 | if (get_u8(&hoplimit, *argv, 0)) | |
297 | invarg("\"hoplimit\" value is invalid\n", *argv); | |
298 | rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit); | |
299 | } else { | |
300 | break; | |
301 | } | |
302 | argc--; argv++; | |
303 | } | |
304 | ||
305 | /* argv is currently the first unparsed argument, | |
306 | * but the lwt_parse_encap() caller will move to the next, | |
307 | * so step back */ | |
308 | *argcp = argc + 1; | |
309 | *argvp = argv - 1; | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
1e529305 RP |
314 | int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) |
315 | { | |
316 | struct rtattr *nest; | |
317 | int argc = *argcp; | |
318 | char **argv = *argvp; | |
319 | __u16 type; | |
320 | ||
321 | NEXT_ARG(); | |
322 | type = read_encap_type(*argv); | |
323 | if (!type) | |
324 | invarg("\"encap type\" value is invalid\n", *argv); | |
325 | ||
326 | NEXT_ARG(); | |
327 | if (argc <= 1) { | |
328 | fprintf(stderr, "Error: unexpected end of line after \"encap\"\n"); | |
329 | exit(-1); | |
330 | } | |
331 | ||
332 | nest = rta_nest(rta, 1024, RTA_ENCAP); | |
333 | switch (type) { | |
334 | case LWTUNNEL_ENCAP_MPLS: | |
335 | parse_encap_mpls(rta, len, &argc, &argv); | |
336 | break; | |
337 | case LWTUNNEL_ENCAP_IP: | |
338 | parse_encap_ip(rta, len, &argc, &argv); | |
339 | break; | |
5866bddd TH |
340 | case LWTUNNEL_ENCAP_ILA: |
341 | parse_encap_ila(rta, len, &argc, &argv); | |
342 | break; | |
d95cdcf5 PA |
343 | case LWTUNNEL_ENCAP_IP6: |
344 | parse_encap_ip6(rta, len, &argc, &argv); | |
345 | break; | |
1e529305 RP |
346 | default: |
347 | fprintf(stderr, "Error: unsupported encap type\n"); | |
348 | break; | |
349 | } | |
350 | rta_nest_end(rta, nest); | |
351 | ||
352 | rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type); | |
353 | ||
354 | *argcp = argc; | |
355 | *argvp = argv; | |
356 | ||
357 | return 0; | |
358 | } |