+static int
+set_tunnel_config(struct netdev *dev_, const struct smap *args)
+{
+ struct netdev_vport *dev = netdev_vport_cast(dev_);
+ const char *name = netdev_get_name(dev_);
+ const char *type = netdev_get_type(dev_);
+ bool ipsec_mech_set, needs_dst_port, has_csum;
+ struct netdev_tunnel_config tnl_cfg;
+ struct smap_node *node;
+
+ has_csum = strstr(type, "gre");
+ ipsec_mech_set = false;
+ memset(&tnl_cfg, 0, sizeof tnl_cfg);
+
+ needs_dst_port = netdev_vport_needs_dst_port(dev_);
+ tnl_cfg.ipsec = strstr(type, "ipsec");
+ tnl_cfg.dont_fragment = true;
+
+ SMAP_FOR_EACH (node, args) {
+ if (!strcmp(node->key, "remote_ip")) {
+ struct in_addr in_addr;
+ if (!strcmp(node->value, "flow")) {
+ tnl_cfg.ip_dst_flow = true;
+ tnl_cfg.ip_dst = htonl(0);
+ } else if (lookup_ip(node->value, &in_addr)) {
+ VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
+ } else if (ip_is_multicast(in_addr.s_addr)) {
+ VLOG_WARN("%s: multicast remote_ip="IP_FMT" not allowed",
+ name, IP_ARGS(in_addr.s_addr));
+ return EINVAL;
+ } else {
+ tnl_cfg.ip_dst = in_addr.s_addr;
+ }
+ } else if (!strcmp(node->key, "local_ip")) {
+ struct in_addr in_addr;
+ if (!strcmp(node->value, "flow")) {
+ tnl_cfg.ip_src_flow = true;
+ tnl_cfg.ip_src = htonl(0);
+ } else if (lookup_ip(node->value, &in_addr)) {
+ VLOG_WARN("%s: bad %s 'local_ip'", name, type);
+ } else {
+ tnl_cfg.ip_src = in_addr.s_addr;
+ }
+ } else if (!strcmp(node->key, "tos")) {
+ if (!strcmp(node->value, "inherit")) {
+ tnl_cfg.tos_inherit = true;
+ } else {
+ char *endptr;
+ int tos;
+ tos = strtol(node->value, &endptr, 0);
+ if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
+ tnl_cfg.tos = tos;
+ } else {
+ VLOG_WARN("%s: invalid TOS %s", name, node->value);
+ }
+ }
+ } else if (!strcmp(node->key, "ttl")) {
+ if (!strcmp(node->value, "inherit")) {
+ tnl_cfg.ttl_inherit = true;
+ } else {
+ tnl_cfg.ttl = atoi(node->value);
+ }
+ } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
+ tnl_cfg.dst_port = htons(atoi(node->value));
+ } else if (!strcmp(node->key, "csum") && has_csum) {
+ if (!strcmp(node->value, "true")) {
+ tnl_cfg.csum = true;
+ }
+ } else if (!strcmp(node->key, "df_default")) {
+ if (!strcmp(node->value, "false")) {
+ tnl_cfg.dont_fragment = false;
+ }
+ } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
+ if (smap_get(args, "certificate")) {
+ ipsec_mech_set = true;
+ } else {
+ const char *use_ssl_cert;
+
+ /* If the "use_ssl_cert" is true, then "certificate" and
+ * "private_key" will be pulled from the SSL table. The
+ * use of this option is strongly discouraged, since it
+ * will like be removed when multiple SSL configurations
+ * are supported by OVS.
+ */
+ use_ssl_cert = smap_get(args, "use_ssl_cert");
+ if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) {
+ VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument",
+ name);
+ return EINVAL;
+ }
+ ipsec_mech_set = true;
+ }
+ } else if (!strcmp(node->key, "psk") && tnl_cfg.ipsec) {
+ ipsec_mech_set = true;
+ } else if (tnl_cfg.ipsec
+ && (!strcmp(node->key, "certificate")
+ || !strcmp(node->key, "private_key")
+ || !strcmp(node->key, "use_ssl_cert"))) {
+ /* Ignore options not used by the netdev. */
+ } else if (!strcmp(node->key, "key") ||
+ !strcmp(node->key, "in_key") ||
+ !strcmp(node->key, "out_key")) {
+ /* Handled separately below. */
+ } else {
+ VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
+ }
+ }