From ed0c68b07ddbc130e033035baa1cc0862e511b46 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Thu, 25 Feb 2010 17:05:45 +0100 Subject: [PATCH] 07_flow-opt-support.diff In order to support a class of socket options which have to be set before commiting a socket to a connection, this patch adds an API for "pre-connection" socket options, called "flowopts" here. The API is used as follows: * to provide queue storage, a new head is allocated using flowopt_new(); * this queue is then populated with pre-connection socket options via - generic 'add' method flowopt_add(), - a specific variant for adding Boolean options - flowopt_add_bool(), - convenience macros - OPT_ADD(), OPT_ENABLE(), OPT_DISABLE(); * the rest is then dealt with automatically by makesock(): - any provided flowopts are set before making the connection, - due to using a queue, the order of invocation is preserved, - after making the connection, makesock() frees storage that has been previously allocated by flowopt_new() and flowopt_add.*(). --- client_common.c | 2 +- dccp_recv.c | 2 +- http_recv.c | 2 +- net.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++-- net.h | 19 +++++++- udp_recv.c | 2 +- udp_send.c | 2 +- 7 files changed, 137 insertions(+), 10 deletions(-) diff --git a/client_common.c b/client_common.c index 1536bb2e..f3c96aad 100644 --- a/client_common.c +++ b/client_common.c @@ -312,7 +312,7 @@ static int client_connect(struct client_task *ct) ct->rc4c.fd = -1; ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, ct->conf.hostname_arg, - ct->conf.server_port_arg); + ct->conf.server_port_arg, NULL); if (ret < 0) return ret; ct->rc4c.fd = ret; diff --git a/dccp_recv.c b/dccp_recv.c index e0d3e234..647d31a6 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -58,7 +58,7 @@ static int dccp_recv_open(struct receiver_node *rn) struct private_dccp_recv_data *pdd; struct dccp_recv_args_info *conf = rn->conf; int fd, ret = makesock(AF_UNSPEC, IPPROTO_DCCP, 0, conf->host_arg, - conf->port_arg); + conf->port_arg, NULL); if (ret < 0) return ret; diff --git a/http_recv.c b/http_recv.c index bd234a02..107bfdcd 100644 --- a/http_recv.c +++ b/http_recv.c @@ -166,7 +166,7 @@ static int http_recv_open(struct receiver_node *rn) struct private_http_recv_data *phd; struct http_recv_args_info *conf = rn->conf; int fd, ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, conf->host_arg, - conf->port_arg); + conf->port_arg, NULL); if (ret < 0) return ret; diff --git a/net.c b/net.c index b83ab8c6..d85321d9 100644 --- a/net.c +++ b/net.c @@ -34,6 +34,7 @@ #include "crypt.h" #include "net.h" #include "string.h" +#include "list.h" #include "fd.h" /** @@ -242,6 +243,111 @@ static const char *layer4_name(const unsigned l4type) return "UNKNOWN PROTOCOL"; } +/** + * 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 \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, + 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, + 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); +} + /** * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. * @@ -250,6 +356,7 @@ static const char *layer4_name(const unsigned l4type) * \param passive Whether this is a passive (1) or active (0) socket. * \param host Remote or local hostname or IPv/6 address string. * \param port_number Decimal port number. + * \param fo Socket options to be set before making the connection. * * This creates a ready-made IPv4/v6 socket structure after looking up the * necessary parameters. The interpretation of \a host depends on the value of @@ -263,7 +370,7 @@ static const char *layer4_name(const unsigned l4type) * * Furthermore, bind(2) is called on passive sockets, and connect(2) on active * sockets. The algorithm tries all possible address combinations until it - * succeeds. + * succeeds. If \a fo is supplied, options are set and cleanup is performed. * * \return This function returns 1 on success and \a -E_ADDRESS_LOOKUP when no * matching connection could be set up (with details in the error log). @@ -271,7 +378,8 @@ static const char *layer4_name(const unsigned l4type) * \sa ipv6(7), getaddrinfo(3), bind(2), connect(2). */ int makesock(unsigned l3type, unsigned l4type, int passive, - const char *host, unsigned short port_number) + const char *host, unsigned short port_number, + struct flowopts *fo) { struct addrinfo *local = NULL, *src, *remote = NULL, *dst, hints; @@ -331,6 +439,7 @@ int makesock(unsigned l3type, unsigned l4type, int passive, strerror(errno)); return -ERRNO_TO_PARA_ERROR(errno); } + flowopt_setopts(sockfd, fo); if (src) { if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) { @@ -355,6 +464,7 @@ get_next_src: freeaddrinfo(local); if (remote) freeaddrinfo(remote); + flowopt_cleanup(fo); if (src == NULL && dst == NULL) { PARA_ERROR_LOG("can not create %s socket %s#%s.\n", @@ -379,7 +489,7 @@ get_next_src: */ int para_listen(unsigned l3type, unsigned l4type, unsigned short port) { - int ret, fd = makesock(l3type, l4type, 1, NULL, port); + int ret, fd = makesock(l3type, l4type, 1, NULL, port, NULL); if (fd > 0) { ret = listen(fd, BACKLOG); @@ -672,7 +782,7 @@ int para_accept(int fd, void *addr, socklen_t size) */ const uint8_t *dccp_available_ccids(uint8_t *ccids, uint8_t *nccids) { - int fd = makesock(AF_UNSPEC, IPPROTO_DCCP, 0, NULL, 0); + int fd = makesock(AF_UNSPEC, IPPROTO_DCCP, 1, NULL, 0, NULL); if (fd < 0) return NULL; diff --git a/net.h b/net.h index 66bf8755..96fa07ee 100644 --- a/net.h +++ b/net.h @@ -31,6 +31,22 @@ /** \endcond */ +/** + * Flowopts: Transport-layer independent encapsulation of socket options + * that need to be registered prior to setting up a connection. + */ +struct flowopts; + +extern struct flowopts *flowopt_new(void); +extern void flowopt_add(struct flowopts *fo, int level, int opt, + char *name, const void *val, int len); +extern void flowopt_add_bool(struct flowopts *fo, int lev, int opt, + char *optname, bool on_or_off); +/** Flowopt shortcut macros */ +#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len) +#define OPT_ENABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 1) +#define OPT_DISABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 0) + /** * Functions to parse and validate (parts of) URLs. */ @@ -72,7 +88,8 @@ _static_inline_ bool is_valid_ipv6_address(const char *address) * Generic socket creation (passive and active sockets). */ extern int makesock(unsigned l3type, unsigned l4type, int passive, - const char *host, unsigned short port_number); + const char *host, unsigned short port_number, + struct flowopts *fo); extern struct in_addr extract_v4_addr(const struct sockaddr_storage *ss); /** diff --git a/udp_recv.c b/udp_recv.c index d899e439..73549453 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -181,7 +181,7 @@ static int udp_recv_open(struct receiver_node *rn) rn->private_data = para_calloc(sizeof(struct private_udp_recv_data)); purd = rn->private_data; - ret = makesock(AF_UNSPEC, IPPROTO_UDP, 1, c->host_arg, c->port_arg); + ret = makesock(AF_UNSPEC, IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL); if (ret < 0) goto err; purd->fd = ret; diff --git a/udp_send.c b/udp_send.c index 7c4a8403..13d2a2ca 100644 --- a/udp_send.c +++ b/udp_send.c @@ -160,7 +160,7 @@ static int udp_init_session(struct udp_target *ut) if (ut->fd >= 0) /* nothing to do */ return 0; - ret = makesock(AF_UNSPEC, IPPROTO_UDP, 0, ut->host, ut->port); + ret = makesock(AF_UNSPEC, IPPROTO_UDP, 0, ut->host, ut->port, NULL); if (ret < 0) return ret; ut->fd = ret; -- 2.39.2