]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/link_gre.c
ip/tunnel: No need to free answer after rtnl_talk() on error
[mirror_iproute2.git] / ip / link_gre.c
1 /*
2 * link_gre.c gre driver module
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: Herbert Xu <herbert@gondor.apana.org.au>
10 *
11 */
12
13 #include <string.h>
14 #include <net/if.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <arpa/inet.h>
18
19 #include <linux/ip.h>
20 #include <linux/if_tunnel.h>
21 #include "rt_names.h"
22 #include "utils.h"
23 #include "ip_common.h"
24 #include "tunnel.h"
25
26 static void print_usage(FILE *f)
27 {
28 fprintf(f,
29 "Usage: ... { gre | gretap | erspan } [ remote ADDR ]\n"
30 " [ local ADDR ]\n"
31 " [ [i|o]seq ]\n"
32 " [ [i|o]key KEY ]\n"
33 " [ [i|o]csum ]\n"
34 " [ ttl TTL ]\n"
35 " [ tos TOS ]\n"
36 " [ [no]pmtudisc ]\n"
37 " [ [no]ignore-df ]\n"
38 " [ dev PHYS_DEV ]\n"
39 " [ noencap ]\n"
40 " [ encap { fou | gue | none } ]\n"
41 " [ encap-sport PORT ]\n"
42 " [ encap-dport PORT ]\n"
43 " [ [no]encap-csum ]\n"
44 " [ [no]encap-csum6 ]\n"
45 " [ [no]encap-remcsum ]\n"
46 " [ fwmark MARK ]\n"
47 " [ erspan IDX ]\n"
48 "\n"
49 "Where: ADDR := { IP_ADDRESS | any }\n"
50 " TOS := { NUMBER | inherit }\n"
51 " TTL := { 1..255 | inherit }\n"
52 " KEY := { DOTTED_QUAD | NUMBER }\n"
53 " MARK := { 0x0..0xffffffff }\n"
54 );
55 }
56
57 static void usage(void) __attribute__((noreturn));
58 static void usage(void)
59 {
60 print_usage(stderr);
61 exit(-1);
62 }
63
64 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
65 struct nlmsghdr *n)
66 {
67 struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
68 struct {
69 struct nlmsghdr n;
70 struct ifinfomsg i;
71 } req = {
72 .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
73 .n.nlmsg_flags = NLM_F_REQUEST,
74 .n.nlmsg_type = RTM_GETLINK,
75 .i.ifi_family = preferred_family,
76 .i.ifi_index = ifi->ifi_index,
77 };
78 struct nlmsghdr *answer;
79 struct rtattr *tb[IFLA_MAX + 1];
80 struct rtattr *linkinfo[IFLA_INFO_MAX+1];
81 struct rtattr *greinfo[IFLA_GRE_MAX + 1];
82 __u16 iflags = 0;
83 __u16 oflags = 0;
84 __be32 ikey = 0;
85 __be32 okey = 0;
86 unsigned int saddr = 0;
87 unsigned int daddr = 0;
88 unsigned int link = 0;
89 __u8 pmtudisc = 1;
90 __u8 ttl = 0;
91 __u8 tos = 0;
92 int len;
93 __u16 encaptype = 0;
94 __u16 encapflags = 0;
95 __u16 encapsport = 0;
96 __u16 encapdport = 0;
97 __u8 metadata = 0;
98 __u8 ignore_df = 0;
99 __u32 fwmark = 0;
100 __u32 erspan_idx = 0;
101
102 if (!(n->nlmsg_flags & NLM_F_CREATE)) {
103 if (rtnl_talk(&rth, &req.n, &answer) < 0) {
104 get_failed:
105 fprintf(stderr,
106 "Failed to get existing tunnel info.\n");
107 return -1;
108 }
109
110 len = answer->nlmsg_len;
111 len -= NLMSG_LENGTH(sizeof(*ifi));
112 if (len < 0)
113 goto get_failed;
114
115 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
116
117 if (!tb[IFLA_LINKINFO])
118 goto get_failed;
119
120 parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
121
122 if (!linkinfo[IFLA_INFO_DATA])
123 goto get_failed;
124
125 parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
126 linkinfo[IFLA_INFO_DATA]);
127
128 if (greinfo[IFLA_GRE_IKEY])
129 ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
130
131 if (greinfo[IFLA_GRE_OKEY])
132 okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
133
134 if (greinfo[IFLA_GRE_IFLAGS])
135 iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
136
137 if (greinfo[IFLA_GRE_OFLAGS])
138 oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
139
140 if (greinfo[IFLA_GRE_LOCAL])
141 saddr = rta_getattr_u32(greinfo[IFLA_GRE_LOCAL]);
142
143 if (greinfo[IFLA_GRE_REMOTE])
144 daddr = rta_getattr_u32(greinfo[IFLA_GRE_REMOTE]);
145
146 if (greinfo[IFLA_GRE_PMTUDISC])
147 pmtudisc = rta_getattr_u8(
148 greinfo[IFLA_GRE_PMTUDISC]);
149
150 if (greinfo[IFLA_GRE_TTL])
151 ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
152
153 if (greinfo[IFLA_GRE_TOS])
154 tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
155
156 if (greinfo[IFLA_GRE_LINK])
157 link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
158
159 if (greinfo[IFLA_GRE_ENCAP_TYPE])
160 encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]);
161 if (greinfo[IFLA_GRE_ENCAP_FLAGS])
162 encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]);
163 if (greinfo[IFLA_GRE_ENCAP_SPORT])
164 encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
165 if (greinfo[IFLA_GRE_ENCAP_DPORT])
166 encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
167
168 if (greinfo[IFLA_GRE_COLLECT_METADATA])
169 metadata = 1;
170
171 if (greinfo[IFLA_GRE_IGNORE_DF])
172 ignore_df =
173 !!rta_getattr_u8(greinfo[IFLA_GRE_IGNORE_DF]);
174
175 if (greinfo[IFLA_GRE_FWMARK])
176 fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]);
177
178 if (greinfo[IFLA_GRE_ERSPAN_INDEX])
179 erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
180
181 free(answer);
182 }
183
184 while (argc > 0) {
185 if (!matches(*argv, "key")) {
186 NEXT_ARG();
187 iflags |= GRE_KEY;
188 oflags |= GRE_KEY;
189 ikey = okey = tnl_parse_key("key", *argv);
190 } else if (!matches(*argv, "ikey")) {
191 NEXT_ARG();
192 iflags |= GRE_KEY;
193 ikey = tnl_parse_key("ikey", *argv);
194 } else if (!matches(*argv, "okey")) {
195 NEXT_ARG();
196 oflags |= GRE_KEY;
197 okey = tnl_parse_key("okey", *argv);
198 } else if (!matches(*argv, "seq")) {
199 iflags |= GRE_SEQ;
200 oflags |= GRE_SEQ;
201 } else if (!matches(*argv, "iseq")) {
202 iflags |= GRE_SEQ;
203 } else if (!matches(*argv, "oseq")) {
204 oflags |= GRE_SEQ;
205 } else if (!matches(*argv, "csum")) {
206 iflags |= GRE_CSUM;
207 oflags |= GRE_CSUM;
208 } else if (!matches(*argv, "icsum")) {
209 iflags |= GRE_CSUM;
210 } else if (!matches(*argv, "ocsum")) {
211 oflags |= GRE_CSUM;
212 } else if (!matches(*argv, "nopmtudisc")) {
213 pmtudisc = 0;
214 } else if (!matches(*argv, "pmtudisc")) {
215 pmtudisc = 1;
216 } else if (!matches(*argv, "remote")) {
217 NEXT_ARG();
218 daddr = get_addr32(*argv);
219 } else if (!matches(*argv, "local")) {
220 NEXT_ARG();
221 saddr = get_addr32(*argv);
222 } else if (!matches(*argv, "dev")) {
223 NEXT_ARG();
224 link = if_nametoindex(*argv);
225 if (link == 0) {
226 fprintf(stderr, "Cannot find device \"%s\"\n",
227 *argv);
228 exit(-1);
229 }
230 } else if (!matches(*argv, "ttl") ||
231 !matches(*argv, "hoplimit")) {
232 unsigned int uval;
233
234 NEXT_ARG();
235 if (strcmp(*argv, "inherit") != 0) {
236 if (get_unsigned(&uval, *argv, 0))
237 invarg("invalid TTL\n", *argv);
238 if (uval > 255)
239 invarg("TTL must be <= 255\n", *argv);
240 ttl = uval;
241 } else
242 ttl = 0;
243 } else if (!matches(*argv, "tos") ||
244 !matches(*argv, "tclass") ||
245 !matches(*argv, "dsfield")) {
246 __u32 uval;
247
248 NEXT_ARG();
249 if (strcmp(*argv, "inherit") != 0) {
250 if (rtnl_dsfield_a2n(&uval, *argv))
251 invarg("bad TOS value", *argv);
252 tos = uval;
253 } else
254 tos = 1;
255 } else if (strcmp(*argv, "noencap") == 0) {
256 encaptype = TUNNEL_ENCAP_NONE;
257 } else if (strcmp(*argv, "encap") == 0) {
258 NEXT_ARG();
259 if (strcmp(*argv, "fou") == 0)
260 encaptype = TUNNEL_ENCAP_FOU;
261 else if (strcmp(*argv, "gue") == 0)
262 encaptype = TUNNEL_ENCAP_GUE;
263 else if (strcmp(*argv, "none") == 0)
264 encaptype = TUNNEL_ENCAP_NONE;
265 else
266 invarg("Invalid encap type.", *argv);
267 } else if (strcmp(*argv, "encap-sport") == 0) {
268 NEXT_ARG();
269 if (strcmp(*argv, "auto") == 0)
270 encapsport = 0;
271 else if (get_u16(&encapsport, *argv, 0))
272 invarg("Invalid source port.", *argv);
273 } else if (strcmp(*argv, "encap-dport") == 0) {
274 NEXT_ARG();
275 if (get_u16(&encapdport, *argv, 0))
276 invarg("Invalid destination port.", *argv);
277 } else if (strcmp(*argv, "encap-csum") == 0) {
278 encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
279 } else if (strcmp(*argv, "noencap-csum") == 0) {
280 encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
281 } else if (strcmp(*argv, "encap-udp6-csum") == 0) {
282 encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
283 } else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
284 encapflags |= ~TUNNEL_ENCAP_FLAG_CSUM6;
285 } else if (strcmp(*argv, "encap-remcsum") == 0) {
286 encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
287 } else if (strcmp(*argv, "noencap-remcsum") == 0) {
288 encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM;
289 } else if (strcmp(*argv, "external") == 0) {
290 metadata = 1;
291 } else if (strcmp(*argv, "ignore-df") == 0) {
292 ignore_df = 1;
293 } else if (strcmp(*argv, "noignore-df") == 0) {
294 /*
295 *only the lsb is significant, use 2 for presence
296 */
297 ignore_df = 2;
298 } else if (strcmp(*argv, "fwmark") == 0) {
299 NEXT_ARG();
300 if (get_u32(&fwmark, *argv, 0))
301 invarg("invalid fwmark\n", *argv);
302 } else if (strcmp(*argv, "erspan") == 0) {
303 NEXT_ARG();
304 if (get_u32(&erspan_idx, *argv, 0))
305 invarg("invalid erspan index\n", *argv);
306 if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
307 invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
308 } else
309 usage();
310 argc--; argv++;
311 }
312
313 if (!ikey && IN_MULTICAST(ntohl(daddr))) {
314 ikey = daddr;
315 iflags |= GRE_KEY;
316 }
317 if (!okey && IN_MULTICAST(ntohl(daddr))) {
318 okey = daddr;
319 oflags |= GRE_KEY;
320 }
321 if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
322 fprintf(stderr, "A broadcast tunnel requires a source address.\n");
323 return -1;
324 }
325
326 if (!metadata) {
327 addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
328 addattr32(n, 1024, IFLA_GRE_OKEY, okey);
329 addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
330 addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
331 addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4);
332 addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4);
333 addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
334 if (link)
335 addattr32(n, 1024, IFLA_GRE_LINK, link);
336 addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
337 addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
338 addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
339 if (erspan_idx != 0)
340 addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
341 } else {
342 addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
343 }
344
345 addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
346 addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
347 addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
348 addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
349
350 if (ignore_df)
351 addattr8(n, 1024, IFLA_GRE_IGNORE_DF, ignore_df & 1);
352
353 return 0;
354 }
355
356 static void gre_print_direct_opt(FILE *f, struct rtattr *tb[])
357 {
358 char s2[64];
359 const char *local = "any";
360 const char *remote = "any";
361 unsigned int iflags = 0;
362 unsigned int oflags = 0;
363
364 if (tb[IFLA_GRE_REMOTE]) {
365 unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]);
366
367 if (addr)
368 remote = format_host(AF_INET, 4, &addr);
369 }
370
371 print_string(PRINT_ANY, "remote", "remote %s ", remote);
372
373 if (tb[IFLA_GRE_LOCAL]) {
374 unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
375
376 if (addr)
377 local = format_host(AF_INET, 4, &addr);
378 }
379
380 print_string(PRINT_ANY, "local", "local %s ", local);
381
382 if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
383 unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
384 const char *n = if_indextoname(link, s2);
385
386 if (n)
387 print_string(PRINT_ANY, "link", "dev %s ", n);
388 else
389 print_uint(PRINT_ANY, "link_index", "dev %u ", link);
390 }
391
392 if (tb[IFLA_GRE_TTL]) {
393 __u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
394
395 if (ttl)
396 print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
397 else
398 print_int(PRINT_JSON, "ttl", NULL, ttl);
399 } else {
400 print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
401 }
402
403 if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) {
404 int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
405
406 if (is_json_context()) {
407 SPRINT_BUF(b1);
408
409 snprintf(b1, sizeof(b1), "0x%x", tos);
410 print_string(PRINT_JSON, "tos", NULL, b1);
411 } else {
412 fputs("tos ", f);
413 if (tos == 1)
414 fputs("inherit ", f);
415 else
416 fprintf(f, "0x%x ", tos);
417 }
418 }
419
420 if (tb[IFLA_GRE_PMTUDISC]) {
421 if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
422 print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
423 else
424 print_bool(PRINT_JSON, "pmtudisc", NULL, true);
425 }
426
427 if (tb[IFLA_GRE_IFLAGS])
428 iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
429
430 if (tb[IFLA_GRE_OFLAGS])
431 oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
432
433 if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
434 inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
435 print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
436 }
437
438 if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
439 inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
440 print_string(PRINT_ANY, "okey", "okey %s ", s2);
441 }
442
443 if (iflags & GRE_SEQ)
444 print_bool(PRINT_ANY, "iseq", "iseq ", true);
445 if (oflags & GRE_SEQ)
446 print_bool(PRINT_ANY, "oseq", "oseq ", true);
447 if (iflags & GRE_CSUM)
448 print_bool(PRINT_ANY, "icsum", "icsum ", true);
449 if (oflags & GRE_CSUM)
450 print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
451
452 if (tb[IFLA_GRE_FWMARK]) {
453 __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
454
455 if (fwmark) {
456 snprintf(s2, sizeof(s2), "0x%x", fwmark);
457
458 print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
459 }
460 }
461 }
462
463 static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
464 {
465 if (!tb)
466 return;
467
468 if (!tb[IFLA_GRE_COLLECT_METADATA])
469 gre_print_direct_opt(f, tb);
470 else
471 print_bool(PRINT_ANY, "external", "external ", true);
472
473 if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF]))
474 print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true);
475
476 if (tb[IFLA_GRE_ERSPAN_INDEX]) {
477 __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
478
479 fprintf(f, "erspan_index %u ", erspan_idx);
480 }
481
482 if (tb[IFLA_GRE_ENCAP_TYPE] &&
483 rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
484 __u16 type = rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]);
485 __u16 flags = rta_getattr_u16(tb[IFLA_GRE_ENCAP_FLAGS]);
486 __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]);
487 __u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]);
488
489
490 open_json_object("encap");
491 print_string(PRINT_FP, NULL, "encap ", NULL);
492
493 switch (type) {
494 case TUNNEL_ENCAP_FOU:
495 print_string(PRINT_ANY, "type", "%s ", "fou");
496 break;
497 case TUNNEL_ENCAP_GUE:
498 print_string(PRINT_ANY, "type", "%s ", "gue");
499 break;
500 default:
501 print_null(PRINT_ANY, "type", "%s ", "unknown");
502 break;
503 }
504
505 if (is_json_context()) {
506 print_uint(PRINT_JSON,
507 "sport",
508 NULL,
509 sport ? ntohs(sport) : 0);
510 print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
511
512 print_bool(PRINT_JSON,
513 "csum",
514 NULL,
515 flags & TUNNEL_ENCAP_FLAG_CSUM);
516
517 print_bool(PRINT_JSON,
518 "csum6",
519 NULL,
520 flags & TUNNEL_ENCAP_FLAG_CSUM6);
521
522 print_bool(PRINT_JSON,
523 "remcsum",
524 NULL,
525 flags & TUNNEL_ENCAP_FLAG_REMCSUM);
526
527 close_json_object();
528 } else {
529 if (sport == 0)
530 fputs("encap-sport auto ", f);
531 else
532 fprintf(f, "encap-sport %u", ntohs(sport));
533
534 fprintf(f, "encap-dport %u ", ntohs(dport));
535
536 if (flags & TUNNEL_ENCAP_FLAG_CSUM)
537 fputs("encap-csum ", f);
538 else
539 fputs("noencap-csum ", f);
540
541 if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
542 fputs("encap-csum6 ", f);
543 else
544 fputs("noencap-csum6 ", f);
545
546 if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
547 fputs("encap-remcsum ", f);
548 else
549 fputs("noencap-remcsum ", f);
550 }
551 }
552 }
553
554 static void gre_print_help(struct link_util *lu, int argc, char **argv,
555 FILE *f)
556 {
557 print_usage(f);
558 }
559
560 struct link_util gre_link_util = {
561 .id = "gre",
562 .maxattr = IFLA_GRE_MAX,
563 .parse_opt = gre_parse_opt,
564 .print_opt = gre_print_opt,
565 .print_help = gre_print_help,
566 };
567
568 struct link_util gretap_link_util = {
569 .id = "gretap",
570 .maxattr = IFLA_GRE_MAX,
571 .parse_opt = gre_parse_opt,
572 .print_opt = gre_print_opt,
573 .print_help = gre_print_help,
574 };
575
576 struct link_util erspan_link_util = {
577 .id = "erspan",
578 .maxattr = IFLA_GRE_MAX,
579 .parse_opt = gre_parse_opt,
580 .print_opt = gre_print_opt,
581 .print_help = gre_print_help,
582 };