diff --git a/ip/link_gre.c b/ip/link_gre.c index e92b0d37..9109312b 100644 --- a/ip/link_gre.c +++ b/ip/link_gre.c @@ -41,12 +41,92 @@ static void usage(void) static int gre_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req; + struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *greinfo[IFLA_GRE_MAX + 1]; __u16 iflags = 0; __u16 oflags = 0; unsigned ikey = 0; unsigned okey = 0; unsigned saddr = 0; unsigned daddr = 0; + unsigned link = 0; + __u8 pmtudisc = 1; + __u8 ttl = 0; + __u8 tos = 0; + int len; + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETLINK; + req.i.ifi_family = preferred_family; + req.i.ifi_index = ifi->ifi_index; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = req.n.nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(greinfo, IFLA_GRE_MAX, + linkinfo[IFLA_INFO_DATA]); + + if (greinfo[IFLA_GRE_IKEY]) + ikey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_IKEY]); + + if (greinfo[IFLA_GRE_OKEY]) + okey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_OKEY]); + + if (greinfo[IFLA_GRE_IFLAGS]) + iflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_IFLAGS]); + + if (greinfo[IFLA_GRE_OFLAGS]) + oflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_OFLAGS]); + + if (greinfo[IFLA_GRE_LOCAL]) + saddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_LOCAL]); + + if (greinfo[IFLA_GRE_REMOTE]) + daddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_REMOTE]); + + if (greinfo[IFLA_GRE_PMTUDISC]) + pmtudisc = *(__u8 *)RTA_DATA( + greinfo[IFLA_GRE_PMTUDISC]); + + if (greinfo[IFLA_GRE_TTL]) + ttl = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TTL]); + + if (greinfo[IFLA_GRE_TOS]) + tos = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TOS]); + + if (greinfo[IFLA_GRE_LINK]) + link = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_LINK]); + } while (argc > 0) { if (!matches(*argv, "key")) { @@ -112,13 +192,9 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, } else if (!matches(*argv, "ocsum")) { oflags |= GRE_CSUM; } else if (!matches(*argv, "nopmtudisc")) { - __u8 val = 0; - - addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &val, 1); + pmtudisc = 0; } else if (!matches(*argv, "pmtudisc")) { - __u8 val = 1; - - addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &val, 1); + pmtudisc = 1; } else if (!matches(*argv, "remote")) { NEXT_ARG(); if (strcmp(*argv, "any")) @@ -128,18 +204,13 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, if (strcmp(*argv, "any")) saddr = get_addr32(*argv); } else if (!matches(*argv, "dev")) { - unsigned link; - NEXT_ARG(); link = tnl_ioctl_get_ifindex(*argv); if (link == 0) exit(-1); - - addattr32(n, 1024, IFLA_GRE_LINK, link); } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { unsigned uval; - __u8 ttl; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { @@ -148,13 +219,11 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, if (uval > 255) invarg("TTL must be <= 255\n", *argv); ttl = uval; - addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); } } else if (!matches(*argv, "tos") || !matches(*argv, "tclass") || !matches(*argv, "dsfield")) { __u32 uval; - __u8 tos; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { @@ -163,7 +232,6 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, tos = uval; } else tos = 1; - addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); } else usage(); argc--; argv++; @@ -188,6 +256,11 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2); addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4); addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4); + addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1); + if (link) + addattr32(n, 1024, IFLA_GRE_LINK, link); + addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); + addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); return 0; }