diff --git a/ChangeLog b/ChangeLog index 54002cb50fb..1d352151272 100755 --- a/ChangeLog +++ b/ChangeLog @@ -10466,3 +10466,12 @@ list AND the device event list. Thus each socket type can received both custom data-related events as well as common device related events (2015-05-28). + * net/socket and net/utils: setsockopt() fails when setting timeouts + to values less that an 100 msec. That is because the timeout is + limited to stops of 1 decisecond and because the conversion of + structure timeval was truncating the microsecond remainder. The + utility net_timeval2dsec now accespts and option to determin how it + handles the remainder: truncate, discarding the remainder, use the + remainder to round to the closed decisecond value, or use any non-zero + remainder to the next larger whole decisecond value. Bug discovered + by Librae (2015-05-29). diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index 61e502ddfd8..cc6d15dec9e 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/setsockopt.c * - * Copyright (C) 2007, 2008, 2011-2012, 2014 Gregory Nutt. All rights + * Copyright (C) 2007, 2008, 2011-2012, 2014-2015 Gregory Nutt. All rights * reserved. * Author: Gregory Nutt * @@ -173,19 +173,22 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, case SO_RCVTIMEO: case SO_SNDTIMEO: { + FAR struct timeval *tv = (FAR struct timeval *)value; socktimeo_t timeo; /* Verify that option is the size of an 'struct timeval'. */ - if (value_len != sizeof(struct timeval)) + if (tv == NULL || value_len != sizeof(struct timeval)) { err = EINVAL; goto errout; } - /* Get the timeout value */ + /* Get the timeout value. Any microsecond remainder will be + * force to the next larger, whole decisecond value. + */ - timeo = (socktimeo_t)net_timeval2dsec((struct timeval *)value); + timeo = (socktimeo_t)net_timeval2dsec(tv, TV2DS_CEIL); /* Save the timeout value */ diff --git a/net/utils/net_timeval2dsec.c b/net/utils/net_timeval2dsec.c index b4beaf4eff2..a08a17e964f 100644 --- a/net/utils/net_timeval2dsec.c +++ b/net/utils/net_timeval2dsec.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/utils/net_timeval2dsec.c * - * Copyright (C) 2007, 2008, 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2008, 2014-2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -57,7 +57,8 @@ * save new timeout values. * * Parameters: - * tv The struct timeval to convert + * tv - The struct timeval to convert + * remainder - Determines how to handler the microsecond remainder * * Returned Value: * The converted value @@ -66,7 +67,26 @@ * ****************************************************************************/ -unsigned int net_timeval2dsec(struct timeval *tv) +unsigned int net_timeval2dsec(FAR struct timeval *tv, + enum tv2ds_remainder_e remainder) { - return (unsigned int)(tv->tv_sec * DSEC_PER_SEC + tv->tv_usec / USEC_PER_DSEC); + unsigned long adjust = 0; + + switch (remainder) + { + default: + case TV2DS_TRUNC: /* Truncate microsecond remainder */ + break; + + case TV2DS_ROUND: /* Round to the nearest full decisecond */ + adjust = (USEC_PER_DSEC / 2); + break; + + case TV2DS_CEIL: /* Force to next larger full decisecond */ + adjust = (USEC_PER_DSEC - 1); + break; + } + + return (unsigned int)(tv->tv_sec * DSEC_PER_SEC + + (tv->tv_usec + adjust) / USEC_PER_DSEC); } diff --git a/net/utils/utils.h b/net/utils/utils.h index 3322f868bf4..951caf8d595 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -1,7 +1,7 @@ /**************************************************************************** * net/utils/utils.h * - * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -51,6 +51,15 @@ * Public Types ****************************************************************************/ +/* These values control the behavior of net_timeval2desc */ + +enum tv2ds_remainder_e +{ + TV2DS_TRUNC = 0, /* Truncate microsecond remainder */ + TV2DS_ROUND, /* Round to the nearest full decisecond */ + TV2DS_CEIL /* Force to next larger full decisecond */ +}; + /**************************************************************************** * Public Data ****************************************************************************/ @@ -128,7 +137,8 @@ unsigned int net_dsec2tick(int dsec); * save new timeout values. * * Parameters: - * tv The struct timeval to convert + * tv - The struct timeval to convert + * remainder - Determines how to handler the microsecond remainder * * Returned Value: * The converted value @@ -137,7 +147,8 @@ unsigned int net_dsec2tick(int dsec); * ****************************************************************************/ -unsigned int net_timeval2dsec(FAR struct timeval *tv); +unsigned int net_timeval2dsec(FAR struct timeval *tv, + enum tv2ds_remainder_e remainder); /**************************************************************************** * Function: net_ipv6_pref2mask