+ 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)
+{
+ struct blob_attr *tb_data[__VXLAN_DATA_ATTR_MAX];
+ struct nl_msg *msg;
+ struct nlattr *linkinfo, *data;
+ struct ifinfomsg iim = { .ifi_family = AF_UNSPEC, };
+ struct blob_attr *cur;
+ int ret = 0;
+
+ if ((cur = tb[TUNNEL_ATTR_DATA]))
+ blobmsg_parse(vxlan_data_attr_list.params, __VXLAN_DATA_ATTR_MAX, tb_data,
+ blobmsg_data(cur), blobmsg_len(cur));
+ else
+ return -EINVAL;
+
+ msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+
+ if (!msg)
+ return -1;
+
+ nlmsg_append(msg, &iim, sizeof(iim), 0);
+
+ nla_put_string(msg, IFLA_IFNAME, name);
+
+ if ((cur = tb_data[VXLAN_DATA_ATTR_MACADDR])) {
+ struct ether_addr *ea = ether_aton(blobmsg_get_string(cur));
+ if (!ea) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ nla_put(msg, IFLA_ADDRESS, ETH_ALEN, ea);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_MTU])) {
+ uint32_t mtu = blobmsg_get_u32(cur);
+ nla_put_u32(msg, IFLA_MTU, mtu);
+ }
+
+ if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ nla_put_string(msg, IFLA_INFO_KIND, "vxlan");
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ if (link)
+ nla_put_u32(msg, IFLA_VXLAN_LINK, link);
+
+ if ((cur = tb_data[VXLAN_DATA_ATTR_ID])) {
+ uint32_t id = blobmsg_get_u32(cur);
+ if (id >= (1u << 24) - 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ nla_put_u32(msg, IFLA_VXLAN_ID, id);
+ }
+
+ 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(msg, IFLA_VXLAN_LOCAL6, 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(msg, IFLA_VXLAN_GROUP6, 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(msg, IFLA_VXLAN_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(msg, IFLA_VXLAN_GROUP, sizeof(inbuf), &inbuf);
+ }
+ }
+
+ uint32_t port = 4789;
+ if ((cur = tb_data[VXLAN_DATA_ATTR_PORT])) {
+ port = blobmsg_get_u32(cur);
+ if (port < 1 || port > 65535) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+ nla_put_u16(msg, IFLA_VXLAN_PORT, htons(port));
+
+ if ((cur = tb[TUNNEL_ATTR_TOS])) {
+ char *str = blobmsg_get_string(cur);
+ unsigned tos = 1;
+
+ if (strcmp(str, "inherit")) {
+ if (!system_tos_aton(str, &tos))
+ return -EINVAL;
+ }
+
+ nla_put_u8(msg, IFLA_VXLAN_TOS, tos);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_TTL])) {
+ uint32_t ttl = blobmsg_get_u32(cur);
+ if (ttl < 1 || ttl > 255) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ nla_put_u8(msg, IFLA_VXLAN_TTL, ttl);
+ }
+
+ nla_nest_end(msg, data);
+ nla_nest_end(msg, linkinfo);
+
+ ret = system_rtnl_call(msg);
+ if (ret)
+ D(SYSTEM, "Error adding vxlan '%s': %d\n", name, ret);
+
+ return ret;
+
+failure:
+ nlmsg_free(msg);
+ return ret;
+}
+#endif
+
+static int system_add_sit_tunnel(const char *name, const unsigned int link, struct blob_attr **tb)
+{
+ struct blob_attr *cur;
+ int ret = 0;
+
+ if (system_add_proto_tunnel(name, IPPROTO_IPV6, link, tb) < 0)
+ return -1;
+
+#ifdef SIOCADD6RD
+ if ((cur = tb[TUNNEL_ATTR_DATA])) {
+ struct blob_attr *tb_data[__SIXRD_DATA_ATTR_MAX];
+ unsigned int mask;
+ struct ip_tunnel_6rd p6;
+
+ blobmsg_parse(sixrd_data_attr_list.params, __SIXRD_DATA_ATTR_MAX, tb_data,
+ blobmsg_data(cur), blobmsg_len(cur));
+
+ memset(&p6, 0, sizeof(p6));
+
+ if ((cur = tb_data[SIXRD_DATA_PREFIX])) {
+ if (!parse_ip_and_netmask(AF_INET6, blobmsg_data(cur),
+ &p6.prefix, &mask) || mask > 128) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ p6.prefixlen = mask;
+ }
+
+ if ((cur = tb_data[SIXRD_DATA_RELAY_PREFIX])) {
+ if (!parse_ip_and_netmask(AF_INET, blobmsg_data(cur),
+ &p6.relay_prefix, &mask) || mask > 32) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ p6.relay_prefixlen = mask;
+ }
+
+ if (tunnel_ioctl(name, SIOCADD6RD, &p6) < 0) {
+ ret = -1;
+ goto failure;
+ }
+ }
+#endif
+
+ return ret;
+
+failure:
+ __system_del_ip_tunnel(name, tb);
+ return ret;
+}
+
+static int system_add_proto_tunnel(const char *name, const uint8_t proto, const unsigned int link, struct blob_attr **tb)
+{
+ struct blob_attr *cur;
+ bool set_df = true;
+ struct ip_tunnel_parm p = {
+ .link = link,
+ .iph = {
+ .version = 4,
+ .ihl = 5,
+ .protocol = proto,
+ }
+ };
+
+ if ((cur = tb[TUNNEL_ATTR_LOCAL]) &&
+ inet_pton(AF_INET, blobmsg_data(cur), &p.iph.saddr) < 1)
+ return -EINVAL;
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE]) &&
+ inet_pton(AF_INET, blobmsg_data(cur), &p.iph.daddr) < 1)
+ return -EINVAL;
+
+ if ((cur = tb[TUNNEL_ATTR_DF]))
+ set_df = blobmsg_get_bool(cur);
+
+ if ((cur = tb[TUNNEL_ATTR_TTL]))
+ p.iph.ttl = blobmsg_get_u32(cur);
+
+ if ((cur = tb[TUNNEL_ATTR_TOS])) {