+ nl_msg_put_be64(options, type, htonll(strtoull(s, NULL, 0)));
+ }
+}
+
+static int
+parse_tunnel_config(const char *name, const char *type,
+ const struct shash *args, struct ofpbuf *options)
+{
+ bool is_gre = false;
+ bool is_ipsec = false;
+ struct shash_node *node;
+ bool ipsec_mech_set = false;
+ ovs_be32 daddr = htonl(0);
+ uint32_t flags;
+
+ flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE;
+ if (!strcmp(type, "gre")) {
+ is_gre = true;
+ } else if (!strcmp(type, "ipsec_gre")) {
+ is_gre = true;
+ is_ipsec = true;
+ flags |= TNL_F_IPSEC;
+ flags &= ~TNL_F_HDR_CACHE;
+ }
+
+ SHASH_FOR_EACH (node, args) {
+ if (!strcmp(node->name, "remote_ip")) {
+ struct in_addr in_addr;
+ if (lookup_ip(node->data, &in_addr)) {
+ VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
+ } else {
+ daddr = in_addr.s_addr;
+ }
+ } else if (!strcmp(node->name, "local_ip")) {
+ struct in_addr in_addr;
+ if (lookup_ip(node->data, &in_addr)) {
+ VLOG_WARN("%s: bad %s 'local_ip'", name, type);
+ } else {
+ nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4,
+ in_addr.s_addr);
+ }
+ } else if (!strcmp(node->name, "tos")) {
+ if (!strcmp(node->data, "inherit")) {
+ flags |= TNL_F_TOS_INHERIT;
+ } else {
+ nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, atoi(node->data));
+ }
+ } else if (!strcmp(node->name, "ttl")) {
+ if (!strcmp(node->data, "inherit")) {
+ flags |= TNL_F_TTL_INHERIT;
+ } else {
+ nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->data));
+ }
+ } else if (!strcmp(node->name, "csum") && is_gre) {
+ if (!strcmp(node->data, "true")) {
+ flags |= TNL_F_CSUM;
+ }
+ } else if (!strcmp(node->name, "df_inherit")) {
+ if (!strcmp(node->data, "true")) {
+ flags |= TNL_F_DF_INHERIT;
+ }
+ } else if (!strcmp(node->name, "df_default")) {
+ if (!strcmp(node->data, "false")) {
+ flags &= ~TNL_F_DF_DEFAULT;
+ }
+ } else if (!strcmp(node->name, "pmtud")) {
+ if (!strcmp(node->data, "false")) {
+ flags &= ~TNL_F_PMTUD;
+ }
+ } else if (!strcmp(node->name, "header_cache")) {
+ if (!strcmp(node->data, "false")) {
+ flags &= ~TNL_F_HDR_CACHE;
+ }
+ } else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
+ if (shash_find(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 = shash_find_data(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->name, "psk") && is_ipsec) {
+ ipsec_mech_set = true;
+ } else if (is_ipsec
+ && (!strcmp(node->name, "certificate")
+ || !strcmp(node->name, "private_key")
+ || !strcmp(node->name, "use_ssl_cert"))) {
+ /* Ignore options not used by the netdev. */
+ } else if (!strcmp(node->name, "key") ||
+ !strcmp(node->name, "in_key") ||
+ !strcmp(node->name, "out_key")) {
+ /* Handled separately below. */
+ } else {
+ VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
+ }
+ }
+
+ if (is_ipsec) {
+ char *file_name = xasprintf("%s/%s", ovs_rundir(),
+ "ovs-monitor-ipsec.pid");
+ pid_t pid = read_pidfile(file_name);
+ free(file_name);
+ if (pid < 0) {
+ VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon",
+ name);
+ return EINVAL;
+ }
+
+ if (shash_find(args, "peer_cert") && shash_find(args, "psk")) {
+ VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name);
+ return EINVAL;
+ }
+
+ if (!ipsec_mech_set) {
+ VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument",
+ name);
+ return EINVAL;
+ }