+#ifdef IFLA_IPTUN_MAX
+#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000)
+static int system_add_gre_tunnel(const char *name, const char *kind,
+ const unsigned int link, struct blob_attr **tb, bool v6)
+{
+ struct nl_msg *nlm;
+ struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, };
+ struct blob_attr *cur;
+ uint32_t ikey = 0, okey = 0, flags = 0, flowinfo = 0;
+ uint16_t iflags = 0, oflags = 0;
+ uint8_t tos = 0;
+ int ret = 0, ttl = 0;
+
+ nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+ if (!nlm)
+ return -1;
+
+ nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+ nla_put_string(nlm, IFLA_IFNAME, name);
+
+ struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO);
+ if (!linkinfo) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ nla_put_string(nlm, IFLA_INFO_KIND, kind);
+ struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA);
+ if (!infodata) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ if (link)
+ nla_put_u32(nlm, IFLA_GRE_LINK, link);
+
+ if ((cur = tb[TUNNEL_ATTR_TTL]))
+ ttl = blobmsg_get_u32(cur);
+
+ if ((cur = tb[TUNNEL_ATTR_TOS])) {
+ char *str = blobmsg_get_string(cur);
+ if (strcmp(str, "inherit")) {
+ unsigned uval;
+
+ if (!system_tos_aton(str, &uval)) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (v6)
+ flowinfo |= htonl(uval << 20) & IP6_FLOWINFO_TCLASS;
+ else
+ tos = uval;
+ } else {
+ if (v6)
+ flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+ else
+ tos = 1;
+ }
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_DATA])) {
+ struct blob_attr *tb_data[__GRE_DATA_ATTR_MAX];
+
+ blobmsg_parse(gre_data_attr_list.params, __GRE_DATA_ATTR_MAX, tb_data,
+ blobmsg_data(cur), blobmsg_len(cur));
+
+ if ((cur = tb_data[GRE_DATA_IKEY])) {
+ if ((ikey = blobmsg_get_u32(cur)))
+ iflags |= GRE_KEY;
+ }
+
+ if ((cur = tb_data[GRE_DATA_OKEY])) {
+ if ((okey = blobmsg_get_u32(cur)))
+ oflags |= GRE_KEY;
+ }
+
+ if ((cur = tb_data[GRE_DATA_ICSUM])) {
+ if (blobmsg_get_bool(cur))
+ iflags |= GRE_CSUM;
+ }
+
+ if ((cur = tb_data[GRE_DATA_OCSUM])) {
+ if (blobmsg_get_bool(cur))
+ oflags |= GRE_CSUM;
+ }
+
+ if ((cur = tb_data[GRE_DATA_ISEQNO])) {
+ if (blobmsg_get_bool(cur))
+ iflags |= GRE_SEQ;
+ }
+
+ if ((cur = tb[GRE_DATA_OSEQNO])) {
+ if (blobmsg_get_bool(cur))
+ oflags |= GRE_SEQ;
+ }
+ }
+
+ if (v6) {
+ struct in6_addr in6buf;
+ if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_GRE_LOCAL, sizeof(in6buf), &in6buf);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_GRE_REMOTE, sizeof(in6buf), &in6buf);
+ }
+ nla_put_u8(nlm, IFLA_GRE_ENCAP_LIMIT, 4);
+
+ if (flowinfo)
+ nla_put_u32(nlm, IFLA_GRE_FLOWINFO, flowinfo);
+
+ if (flags)
+ nla_put_u32(nlm, IFLA_GRE_FLAGS, flags);
+
+ if (!ttl)
+ ttl = 64;
+ } else {
+ struct in_addr inbuf;
+ bool set_df = true;
+
+ if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+ if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_GRE_LOCAL, sizeof(inbuf), &inbuf);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+ if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_GRE_REMOTE, sizeof(inbuf), &inbuf);
+
+ if (IN_MULTICAST(ntohl(inbuf.s_addr))) {
+ if (!okey) {
+ okey = inbuf.s_addr;
+ oflags |= GRE_KEY;
+ }
+
+ if (!ikey) {
+ ikey = inbuf.s_addr;
+ iflags |= GRE_KEY;
+ }
+ }
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_DF]))
+ set_df = blobmsg_get_bool(cur);
+
+ if (!set_df) {
+ /* ttl != 0 and nopmtudisc are incompatible */
+ if (ttl) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ } else if (!ttl)
+ ttl = 64;
+
+ nla_put_u8(nlm, IFLA_GRE_PMTUDISC, set_df ? 1 : 0);
+
+ nla_put_u8(nlm, IFLA_GRE_TOS, tos);
+ }
+
+ if (ttl)
+ nla_put_u8(nlm, IFLA_GRE_TTL, ttl);
+
+ if (oflags)
+ nla_put_u16(nlm, IFLA_GRE_OFLAGS, oflags);
+
+ if (iflags)
+ nla_put_u16(nlm, IFLA_GRE_IFLAGS, iflags);
+
+ if (okey)
+ nla_put_u32(nlm, IFLA_GRE_OKEY, okey);
+
+ if (ikey)
+ nla_put_u32(nlm, IFLA_GRE_IKEY, ikey);
+
+ nla_nest_end(nlm, infodata);
+ nla_nest_end(nlm, linkinfo);
+
+ return system_rtnl_call(nlm);
+
+failure:
+ nlmsg_free(nlm);
+ return ret;
+}
+#endif
+
+#ifdef IFLA_VTI_MAX
+static int system_add_vti_tunnel(const char *name, const char *kind,
+ const unsigned int link, struct blob_attr **tb, bool v6)
+{
+ struct nl_msg *nlm;
+ struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, };
+ struct blob_attr *cur;
+ int ret = 0;
+
+ nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+ if (!nlm)
+ return -1;
+
+ nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+ nla_put_string(nlm, IFLA_IFNAME, name);
+
+ struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO);
+ if (!linkinfo) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ nla_put_string(nlm, IFLA_INFO_KIND, kind);
+ struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA);
+ if (!infodata) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ if (link)
+ nla_put_u32(nlm, IFLA_VTI_LINK, link);
+
+ if (v6) {
+ struct in6_addr in6buf;
+ if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_VTI_LOCAL, sizeof(in6buf), &in6buf);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_VTI_REMOTE, sizeof(in6buf), &in6buf);
+ }
+
+ } else {
+ struct in_addr inbuf;
+
+ if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+ if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_VTI_LOCAL, sizeof(inbuf), &inbuf);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+ if (inet_pton(AF_INET, blobmsg_data(cur), &inbuf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_VTI_REMOTE, sizeof(inbuf), &inbuf);
+ }
+
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_DATA])) {
+ struct blob_attr *tb_data[__VTI_DATA_ATTR_MAX];
+ uint32_t ikey = 0, okey = 0;
+
+ blobmsg_parse(vti_data_attr_list.params, __VTI_DATA_ATTR_MAX, tb_data,
+ blobmsg_data(cur), blobmsg_len(cur));
+
+ if ((cur = tb_data[VTI_DATA_IKEY])) {
+ if ((ikey = blobmsg_get_u32(cur)))
+ nla_put_u32(nlm, IFLA_VTI_IKEY, htonl(ikey));
+ }
+
+ if ((cur = tb_data[VTI_DATA_OKEY])) {
+ if ((okey = blobmsg_get_u32(cur)))
+ nla_put_u32(nlm, IFLA_VTI_OKEY, htonl(okey));
+ }
+ }
+
+ nla_nest_end(nlm, infodata);
+ nla_nest_end(nlm, linkinfo);
+
+ return system_rtnl_call(nlm);
+
+failure:
+ nlmsg_free(nlm);
+ return ret;
+}
+#endif
+
+#ifdef IFLA_VXLAN_MAX
+static int system_add_vxlan(const char *name, const unsigned int link, struct blob_attr **tb, bool v6)