int dccp_listen_simple(char *service_spec); /* listen on the port associated with service code */This works for both IPv4 and IPv6 sockets and supports all the `SC:', `SC=', and `SC=x' conventions for specifying service codes from RFC 4340, section 8.1.2. Parsing the service code, resolving associated port number and address are all taken care of by the library functions.
int dccp_connect_simple(char *peer, char *service_spec); /* connect to peer using service code string */
int listen_simple(l4proto transport, const uint16_t port, struct flowopts *fo); /* listen on given port */These work in the same way for IPv4/IPv6 hosts, the `l4proto' is an enum, where you are free to use either of the IPPROTO_XXX constants, or the provided shortcuts (`udp', `tcp', `udplite', `dccp') as specifier.
int connect_simple(l4proto transport, char *peer, uint16_t port, struct flowopts *fo); /* connect peer on given port */
listenfd = listen_simple(dccp, HELLO_PORT, NULL); /* server */When no service code is specified, the API uses a zero socket code. This works without problem, especially on internal LANs. If you are crossing NATs or firewalls, people tend to have differing opinions about omitting the service code.
sockfd = connect_simple(dccp, hostname, HELLO_PORT, NULL); /* client */
int makesock(const af_type af, const l4proto transport, int passive,The af_type is one of AF_INET, AF_INET6, or AF_UNSPEC; `transport' is as above; and the `passive' flag has the following meaning:
const char *host, const char *service, struct flowopts *fo);
int makesock2(const af_type af, /* network layer type */You may want to do this to e.g. restrict incoming connections or to force a local interface address when connecting.
const l4proto transport, /* transport layer type */
const char *local_name, /* address to bind() */
const char *local_serv, /* port or service name */
const char *remote_name, /* peer to connect() to */
const char *remote_serv, /* port or service name */
struct flowopts *fo); /* pre-connection options */
void flowopt_add(struct flowopts *fo, /* is filled in by this function */The point of using a character argument (`name') is for error reporting: if setting the socket option fails, the error message will use the value of `name' to indicate which socket option could not be set (there will be likely more than one). Since it is tedious to always fill in the `name' argument, the following shortcuts are provided, which do this automatically:
int level, /* socket level, same as second arg of setsocktopt() */
int opt, /* option type, same as third arg of setsockopt() */
char *name, /* for debugging, should be stringified name of `opt' */
const void *val, /* option value, same as fourth arg of setsockopt() */
int len); /* value-length, same as fifth arg of setsockopt() */
/*
* Wrapper for Boolean socket options (those which are either on=1 or off=0)
*/
void flowopt_add_bool(struct flowopts *fo, int lev, int opt, char *optname, bool on_or_off);
/* shortcuts for the above */ #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)To illustrate, here is an example from the sock program internals:
struct flowopts *fo = flowopt_new();The last line also shows how partial checksums are set on the socket - the same function is used for both DCCP and UDP-Lite (see below for details).
if (broadcast) OPT_ENABLE(fo, SOL_SOCKET, SO_BROADCAST);
if (reuseaddr) OPT_ENABLE(fo, SOL_SOCKET, SO_REUSEADDR); if (rcvbuflen) OPT_ADD(fo, SOL_SOCKET, SO_RCVBUF, &rcvbuflen, sizeof(int)); if (cscov >= 0) /* partial checksum coverage length */ flowopt_set_cscov(fo, l4type, client, &cscov);
void flowopt_set_service(struct flowopts *fo, char *service);The difference to the routines above is that `service' not only supports the `SC' conventions from RFC 4340, 8.1.2, but can optionally be a comma-separated list of such service code strings. These are internally parsed, converted to service codes, and assigned on the socket descriptor. More information is in the next section.
For reference, here is the internal structure contained within the flowopt structure.
struct dccp_services {
uint32_t sc_vec; /* array starts here */
uint8_t sc_len, /* total length of allocated places */
sc_idx; /* stack pointer */
};
For active sockets, sc_vec has exactly one element, and sc_idx is never greater than 1. For passive sockets (which may be offering different services on the same socket), the maximum number is limited by the DCCP_SERVICE_LIST_MAX_LEN constant in linux/dccp.h (currently 32).
It is important to note that service codes are always filled in in network-byte order. The provided library routines automatically take care of this. There are two constructor routines:
struct dccp_services *dccp_service_allocate(uint8_t num_entries); struct dccp_services *dccp_services_new(const uint32_t *sc_arr, const uint8_t len);
The first allocates space for num_entries entries, the second is a copy constructor which copies 'len' entries from the sc_arr array. To populate the allocated structure with service codes, use:
void dccp_service_add(struct dccp_services *ds, uint32_t service_code);The code will not allow pushing more than the pre-allocated num_entries (will bail out with error messages). Note that in the above service_code is an actual number; service string parsing routines are below.
int dccp_services_assign(int sockfd, struct dccp_services const *ds);
To clean up after usage, the following destructor is used:
void dccp_services_cleanup(struct dccp_services *ds);
NB:
For normal
use, these low-level
routines are not necessary. Use the above flowopts wrappers, which are
high-level abstractions and already combine these low-level operations.
uint32_t parse_service_code(const char *service);This function supports all the SC... variants defined in section 8.1.2 of RFC 4340.
There are three pretty-printing routines:
char *assigned_services(int sockfd); char *service_code(uint32_t sc); char *service_codes(uint32_t sc_array[], uint8_t arraylen);
The first of these returns a
comma-separated list of all service codes associated with the
given socket descriptor.
The two other ones are
more low-level and turn a 32-bit service code
(in host-byte-order) into a presentable string - using the ASCII
representation SC:XXXX
if
possible. The
function is simply the array variant of service_codes()
.service_code()
uint16_t dccp_port_from_service_code(uint32_t sc);
int dccp_get_cur_mps(int sockfd);
Note that this requires a connected socket, i.e. after connect() or after accept(). This feature requires
kernel support (available in the test
tree).
getsockopt()
-only routines
which return a struct
tfrc_{r,t}x_info (defined in linux/tfrc.h). To
perform the corresponding getsockopt
calls, these two wrappers are
provided:
void tfrc_get_tx_info(int sockfd, struct tfrc_tx_info *tfrc); void tfrc_get_rx_info(int sockfd, struct tfrc_rx_info *tfrc);
Note that on CCID2 these will not return anything - but will print
warnings if you try to.
Two transport protocols of this library support the use of partial
checksums - UDP-Lite (RFC 3828) and DCCP (RFC 4340, sec. 9).
To test this dynamically (e.g. in a program supporting multiple layer-4
protocols), use
int supports_partial_csums(const l4proto transport);which returns 1 if supported. The use of the flowopts structure is encouraged, as the same wrapper function serves both for UDP-Lite and DCCP partial checksum coverage:
void flowopt_set_cscov(struct flowopts *fo, /* is filled in by this function */ l4proto transport, /* IPPROTO_DCCP/dccp or IPPROTO_UDPLITE/udplite */ int sender, /* set to 1 if you want sender coverage, 0 for receiver coverage */ int const *cscov); /* semantics of this value depend on the transport protocol */Common to both UDP-Lite and DCCP is the distinction between sender and receiver coverage:
int get_cscov(int sockfd, l4proto transport, int sender); /* just return the number (or error) */ char *cscov_to_str(int sockfd, l4proto transport, int sender); /* pretty-print cscov semantics */Both are transport-layer independent, the second generates verbose information about the current coverage.
int is_connection_oriented(const l4proto transport);TCP is connection-oriented and not datagram-based; UDP is connectionless and datagram-based (so is UDP-Lite), while DCCP is connection-oriented and datagram-based.
int is_connectionless(const l4proto transport);
int is_datagram_based(const l4proto transport);
int local_AF(int sockfd); /* returns AF_INET or AF_INET6 */Lastly, from the transport protocol derive other parameters often used in other library calls:
int remote_AF(int sockfd); /* same but for the remote end */
int sock_type(const l4proto transport); /* SOCK_DGRAM | SOCK_STREAM | SOCK_DCCP */
int sock_level(const l4proto transport); /* SOL_DCCP | SOL_UDPLITE | ... */
char *local_name(int sockfd); /* local end of sock */Each of these uses the `host#port' format. The last one pretty-prints v4/v6 socket structures.
char *remote_name(int sockfd); /* remote end of sock */
char *host_and_port(struct sockaddr *sa, socklen_t len);
const char *ADDRESS_FAMILY(const int af); /* prints AF_XXX */
const char *layer3_name(const af_type af); /* v4-only | v6-only | both v4/v6 */
const char *layer4_name(const l4proto transport); /* vernacular name of transport protocol */
void err_ret(const char fmt, ...); /* non-fatal syscall error, continue */
void err_sys(const char fmt, ...); /* fatal syscall error, abort execution */
void err_dump(const char fmt, ...); /* fatal syscall error, dump core */
void err_msg(const char fmt, ...); /* same as fprintf(stder, ...) */
void err_quit(const char fmt, ...); /* same as err_msg(...); exit(1); */
#define die(fmt, args...) err_sys("%s: " fmt, __FUNCTION__, ##args)
#define warn(args...) err_ret("WARNING: " args)
#define warnx(args...) err_msg("WARNING: " args)
However, the main difference to the UNP routines is in the use of linux_strerror()
, which
is very useful for debugging - instead of just interpreting errno in
perror(3) style, it
additionally prints the errno, e.g. "EPERM:
Operation not permitted".
Finally, getting tired to always test for NULL after calling malloc(3), here is a wrapper
void *do_malloc(size_t size);
which does this automatically and complains if there is not enough memory available.