+/**
+ * Flowopts: Transport-layer independent encapsulation of socket options.
+ *
+ * These collect individual socket options into a queue, which is disposed of
+ * directly after makesock(). The 'pre_conn_opt' structure is for internal use
+ * only and should not be visible elsewhere.
+ *
+ * \sa setsockopt(2), makesock()
+ */
+struct pre_conn_opt {
+ int sock_level; /**< Second argument to setsockopt() */
+ int sock_option; /**< Third argument to setsockopt() */
+ char *opt_name; /**< Stringified \a sock_option */
+ void *opt_val; /**< Fourth argument to setsockopt() */
+ socklen_t opt_len; /**< Fifth argument to setsockopt() */
+
+ struct list_head node; /**< FIFO, as sockopt order matters. */
+};
+
+/** FIFO list of pre-connection socket options to be set */
+struct flowopts {
+ struct list_head sockopts;
+};
+
+struct flowopts *flowopt_new(void)
+{
+ struct flowopts *new = para_malloc(sizeof(*new));
+
+ INIT_LIST_HEAD(&new->sockopts);
+ return new;
+}
+
+/**
+ * Append new socket option to flowopt queue.
+ *
+ * \param fo The flowopt queue to append to.
+ * \param lev Level at which \a opt resides.
+ * \param opt New option to add.
+ * \param name Stringified name of \a opt.
+ * \param val The value to set \a opt to.
+ * \param len Length of \a val.
+ *
+ * \sa setsockopt(2)
+ */
+void flowopt_add(struct flowopts *fo, int lev, int opt,
+ const char *name, const void *val, int len)
+{
+ struct pre_conn_opt *new = para_malloc(sizeof(*new));
+
+ new->sock_option = opt;
+ new->sock_level = lev;
+ new->opt_name = para_strdup(name);
+
+ if (val == NULL) {
+ new->opt_val = NULL;
+ new->opt_len = 0;
+ } else {
+ new->opt_val = para_malloc(len);
+ new->opt_len = len;
+ memcpy(new->opt_val, val, len);
+ }
+
+ list_add_tail(&new->node, &fo->sockopts);
+}
+
+void flowopt_add_bool(struct flowopts *fo, int lev, int opt,
+ const char *optname, bool on_or_off)
+{
+ int on = on_or_off; /* kernel takes 'int' */
+
+ flowopt_add(fo, lev, opt, optname, &on, sizeof(on));
+}
+
+/** Set the entire bunch of pre-connection options at once. */
+static void flowopt_setopts(int sockfd, struct flowopts *fo)
+{
+ struct pre_conn_opt *pc;
+
+ if (fo == NULL)
+ return;
+
+ list_for_each_entry(pc, &fo->sockopts, node)
+ if (setsockopt(sockfd, pc->sock_level, pc->sock_option,
+ pc->opt_val, pc->opt_len) < 0) {
+ PARA_EMERG_LOG("Can not set %s socket option: %s",
+ pc->opt_name, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void flowopt_cleanup(struct flowopts *fo)
+{
+ struct pre_conn_opt *cur, *next;
+
+ if (fo == NULL)
+ return;
+
+ list_for_each_entry_safe(cur, next, &fo->sockopts, node) {
+ free(cur->opt_name);
+ free(cur->opt_val);
+ free(cur);
+ }
+ free(fo);
+}
+