From: Phil Pennock Date: Fri, 1 Jun 2012 16:05:42 +0000 (-0400) Subject: DSCP support, tentative X-Git-Tag: exim-4_81_RC1~98 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/9e4f596276eaa20045bdeb4bee2bf31438bfa0d3 DSCP support, tentative --- diff --git a/src/src/functions.h b/src/src/functions.h index 5616db2de..dd9549b18 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -109,6 +109,7 @@ extern int dns_lookup(dns_answer *, uschar *, int, uschar **); extern int dns_special_lookup(dns_answer *, uschar *, int, uschar **); extern dns_record *dns_next_rr(dns_answer *, dns_scan *, int); extern uschar *dns_text_type(int); +extern BOOL dscp_lookup(const uschar *, int, int *, int *, int *); extern void enq_end(uschar *); extern BOOL enq_start(uschar *); @@ -290,7 +291,7 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *, extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); -extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL); +extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar *); extern int smtp_feof(void); extern int smtp_ferror(void); extern uschar *smtp_get_connection_info(void); diff --git a/src/src/ip.c b/src/src/ip.c index 8dbc2b3e0..66a750dfd 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -359,4 +359,114 @@ return -1; } + + +/************************************************* +* Lookup DSCP settings for a socket * +*************************************************/ + +struct dscp_name_tableentry { + const uschar *name; + int value; +}; +/* Keep both of these tables sorted! */ +static struct dscp_name_tableentry dscp_table[] = { +#ifdef IPTOS_DSCP_AF11 + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af21", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "ef", IPTOS_DSCP_EF }, +#endif +#ifdef IPTOS_LOWCOST + { "lowcost", IPTOS_LOWCOST }, +#endif + { "lowdelay", IPTOS_LOWDELAY }, +#ifdef IPTOS_MINCOST + { "mincost", IPTOS_MINCOST }, +#endif + { "reliability", IPTOS_RELIABILITY }, + { "throughput", IPTOS_THROUGHPUT } +}; +static int dscp_table_size = + sizeof(dscp_table) / sizeof(struct dscp_name_tableentry); + +/* DSCP values change by protocol family, and so do the options used for +setsockopt(); this utility does all the lookups. + +Arguments: + dscp_name a string, so far unvalidated + af address_family in use + level setsockopt level to use + optname setsockopt name to use + dscp_value value for dscp_name + +Returns: TRUE if okay to setsockopt(), else FALSE +*/ + +BOOL +dscp_lookup(const uschar *dscp_name, int af, + int *level, int *optname, int *dscp_value) +{ +uschar *dscp_lookup; +int first, last; + +if (af == AF_INET) + { + *level = IPPROTO_IP; + *optname = IP_TOS; + } +else if (af == AF_INET6) + { + *level = IPPROTO_IPV6; + *optname = IPV6_TCLASS; + } +else + { + DEBUG(D_transport) + debug_printf("Unhandled address family %d in dscp_lookup()\n", af); + return FALSE; + } +if (!dscp_name) + { + DEBUG(D_transport) + debug_printf("[empty DSCP]\n"); + return FALSE; + } +dscp_lookup = expand_string(US dscp_name); +if (dscp_lookup == NULL || *dscp_lookup == '\0') + return FALSE; + +first = 0; +last = dscp_table_size; +while (last > first) + { + int middle = (first + last)/2; + int c = Ustrcmp(dscp_lookup, dscp_table[middle].name); + if (c == 0) + { + *dscp_value = dscp_table[middle].value; + return TRUE; + } + else if (c > 0) + { + first = middle + 1; + } + else + { + last = middle; + } + } +return FALSE; +} + + /* End of ip.c */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index d6cfbba7d..7b58b4a74 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -164,16 +164,20 @@ Arguments: interface outgoing interface address or NULL timeout timeout value or 0 keepalive TRUE to use keepalive + dscp DSCP value to assign to socket Returns: connected socket number, or -1 with errno set */ int smtp_connect(host_item *host, int host_af, int port, uschar *interface, - int timeout, BOOL keepalive) + int timeout, BOOL keepalive, const uschar *dscp) { int on = 1; int save_errno = 0; +int dscp_value; +int dscp_level; +int dscp_option; int sock; if (host->port != PORT_NONE) @@ -202,6 +206,25 @@ if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (uschar *)(&on), sizeof(on)); +/* Set DSCP value, if we can. For now, if we fail to set the value, we don't +bomb out, just log it and continue in default traffic class. */ + +if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value)) + { + HDEBUG(D_transport|D_acl|D_v) + debug_printf("DSCP \"%s\"=%d ", dscp, dscp_value); + if (setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)) < 0) + HDEBUG(D_transport|D_acl|D_v) + debug_printf("failed to set DSCP: %s ", strerror(errno)); + /* If the kernel supports IPv4 and IPv6 on an IPv6 socket, we need to set the + option for both; ignore failures here */ + if (host_af == AF_INET6 && + dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value)) + { + (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)); + } + } + /* Bind to a specific interface if requested. Caller must ensure the interface is the same type (IPv4 or IPv6) as the outgoing address. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index b3856f553..5322cc58d 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -55,6 +55,8 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, { "dns_search_parents", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_search_parents) }, + { "dscp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dscp) }, { "fallback_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, fallback_hosts) }, { "final_timeout", opt_time, @@ -162,6 +164,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* interface */ NULL, /* port */ US"smtp", /* protocol */ + NULL, /* DSCP */ NULL, /* serialize_hosts */ NULL, /* hosts_try_auth */ NULL, /* hosts_require_auth */ @@ -945,7 +948,7 @@ if (continue_hostname == NULL) { inblock.sock = outblock.sock = smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive); /* This puts port into host->port */ + ob->keepalive, ob->dscp); /* This puts port into host->port */ if (inblock.sock < 0) { diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 17b75cf3b..067681118 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -17,6 +17,7 @@ typedef struct { uschar *interface; uschar *port; uschar *protocol; + uschar *dscp; uschar *serialize_hosts; uschar *hosts_try_auth; uschar *hosts_require_auth; diff --git a/src/src/verify.c b/src/src/verify.c index 475f52d92..fed6ae37d 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -473,7 +473,8 @@ for (host = host_list; host != NULL && !done; host = host->next) set the error for the last one. Use the callout_connect timeout. */ inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE); + smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL); + /* reconsider DSCP here */ if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s",