On Tue, Apr 19, 2022 at 10:49:44PM +0200, Alexander Bluhm wrote:
> Hi,
>
> I had a look in route timer queues in netinet and netinet6 and found
> some inconsistencies.
>
> - Timeout was a mixture of int, u_int and long. Make timeout
> int with sysctl bounds checking and make absolute time time_t.
>
> - Some code assumes that ..._timeout_q can be NULL and at some
> places this is checked. Better make sure that all queues always
> exist. The pool_get is only called from initialization and from
> syscall, so PR_WAITOK is possible.
>
> - The only special hack I kept is when ip_mtudisc is set to 0.
> Then I destroy the queue and generate an empty one.
>
> - If redirect timeout is 0, it does not time out. Adopt IPv6 to
> behavior of IPv4.
>
> - sysctl net.inet6.icmp6.redirtimeout had no effect as the queue
> timeout was not modified. Make icmp6_sysctl() look like
> icmp_sysctl().
>
> ok?
Looks good to me. OK claudio@
Btw. the rt_timer implementation of rt_timer_queue_change() has a bug.
Actually it is rt_timer_timer() that has the bug. It assumes that the
TAILQ of all rttimers on a queue are sorted but that is not true if the
timeout is lowered in which case objects may live longer then expected.
There are other things in the rt_timer implementation that is questionable
like how rt_expire is changed.
> bluhm
>
> Index: net/route.c
> ===================================================================
> RCS file: /cvs/src/sys/net/route.c,v
> retrieving revision 1.404
> diff -u -p -r1.404 route.c
> --- net/route.c 19 Apr 2022 19:19:31 -0000 1.404
> +++ net/route.c 19 Apr 2022 20:31:49 -0000
> @@ -1399,13 +1399,11 @@ rt_timer_init(void)
> }
>
> struct rttimer_queue *
> -rt_timer_queue_create(u_int timeout)
> +rt_timer_queue_create(int timeout)
> {
> struct rttimer_queue *rtq;
>
> - rtq = pool_get(&rttimer_queue_pool, PR_NOWAIT | PR_ZERO);
> - if (rtq == NULL)
> - return (NULL);
> + rtq = pool_get(&rttimer_queue_pool, PR_WAITOK | PR_ZERO);
>
> rtq->rtq_timeout = timeout;
> rtq->rtq_count = 0;
> @@ -1416,7 +1414,7 @@ rt_timer_queue_create(u_int timeout)
> }
>
> void
> -rt_timer_queue_change(struct rttimer_queue *rtq, long timeout)
> +rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
> {
> rtq->rtq_timeout = timeout;
> }
> @@ -1470,10 +1468,10 @@ rt_timer_add(struct rtentry *rt, void (*
> struct rttimer *), struct rttimer_queue *queue, u_int rtableid)
> {
> struct rttimer *r;
> - long current_time;
> + time_t current_time;
>
> current_time = getuptime();
> - rt->rt_expire = getuptime() + queue->rtq_timeout;
> + rt->rt_expire = current_time + queue->rtq_timeout;
>
> /*
> * If there's already a timer with this action, destroy it before
> @@ -1514,7 +1512,7 @@ rt_timer_timer(void *arg)
> struct timeout *to = (struct timeout *)arg;
> struct rttimer_queue *rtq;
> struct rttimer *r;
> - long current_time;
> + time_t current_time;
>
> current_time = getuptime();
>
> Index: net/route.h
> ===================================================================
> RCS file: /cvs/src/sys/net/route.h,v
> retrieving revision 1.188
> diff -u -p -r1.188 route.h
> --- net/route.h 19 Apr 2022 15:44:56 -0000 1.188
> +++ net/route.h 19 Apr 2022 20:31:49 -0000
> @@ -411,10 +411,10 @@ struct rttimer {
> };
>
> struct rttimer_queue {
> - long rtq_timeout;
> - unsigned long rtq_count;
> TAILQ_HEAD(, rttimer) rtq_head;
> LIST_ENTRY(rttimer_queue) rtq_link;
> + unsigned long rtq_count;
> + int rtq_timeout;
> };
>
> const char *rtlabel_id2name(u_int16_t);
> @@ -456,8 +456,8 @@ int rt_timer_add(struct rtentry *,
> void(*)(struct rtentry *, struct rttimer *),
> struct rttimer_queue *, u_int);
> void rt_timer_remove_all(struct rtentry *);
> -struct rttimer_queue *rt_timer_queue_create(u_int);
> -void rt_timer_queue_change(struct rttimer_queue *, long);
> +struct rttimer_queue *rt_timer_queue_create(int);
> +void rt_timer_queue_change(struct rttimer_queue *, int);
> void rt_timer_queue_destroy(struct rttimer_queue *);
> unsigned long rt_timer_queue_count(struct rttimer_queue *);
> void rt_timer_timer(void *);
> Index: netinet/ip_icmp.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/ip_icmp.c,v
> retrieving revision 1.187
> diff -u -p -r1.187 ip_icmp.c
> --- netinet/ip_icmp.c 26 Jul 2021 20:44:44 -0000 1.187
> +++ netinet/ip_icmp.c 19 Apr 2022 20:31:50 -0000
> @@ -120,7 +120,7 @@ int icmp_redirtimeout = 10 * 60;
> static int icmperrpps_count = 0;
> static struct timeval icmperrppslim_last;
>
> -static struct rttimer_queue *icmp_redirect_timeout_q = NULL;
> +struct rttimer_queue *icmp_redirect_timeout_q;
> struct cpumem *icmpcounters;
>
> const struct sysctl_bounded_args icmpctl_vars[] = {
> @@ -141,15 +141,8 @@ int icmp_sysctl_icmpstat(void *, size_t
> void
> icmp_init(void)
> {
> + icmp_redirect_timeout_q = rt_timer_queue_create(icmp_redirtimeout);
> icmpcounters = counters_alloc(icps_ncounters);
> - /*
> - * This is only useful if the user initializes redirtimeout to
> - * something other than zero.
> - */
> - if (icmp_redirtimeout != 0) {
> - icmp_redirect_timeout_q =
> - rt_timer_queue_create(icmp_redirtimeout);
> - }
> }
>
> struct mbuf *
> @@ -640,12 +633,11 @@ reflect:
> #endif
> rtredirect(sintosa(&sdst), sintosa(&sgw),
> sintosa(&ssrc), &newrt, m->m_pkthdr.ph_rtableid);
> - if (newrt != NULL && icmp_redirtimeout != 0) {
> - (void)rt_timer_add(newrt, icmp_redirect_timeout,
> + if (newrt != NULL && icmp_redirtimeout > 0) {
> + rt_timer_add(newrt, icmp_redirect_timeout,
> icmp_redirect_timeout_q, m->m_pkthdr.ph_rtableid);
> }
> - if (newrt != NULL)
> - rtfree(newrt);
> + rtfree(newrt);
> pfctlinput(PRC_REDIRECT_HOST, sintosa(&sdst));
> break;
> }
> @@ -889,21 +881,11 @@ icmp_sysctl(int *name, u_int namelen, vo
>
> switch (name[0]) {
> case ICMPCTL_REDIRTIMEOUT:
> -
> NET_LOCK();
> - error = sysctl_int(oldp, oldlenp, newp, newlen,
> - &icmp_redirtimeout);
> - if (icmp_redirect_timeout_q != NULL) {
> - if (icmp_redirtimeout == 0) {
> - rt_timer_queue_destroy(icmp_redirect_timeout_q);
> - icmp_redirect_timeout_q = NULL;
> - } else
> - rt_timer_queue_change(icmp_redirect_timeout_q,
> - icmp_redirtimeout);
> - } else if (icmp_redirtimeout > 0) {
> - icmp_redirect_timeout_q =
> - rt_timer_queue_create(icmp_redirtimeout);
> - }
> + error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
> + &icmp_redirtimeout, 0, INT_MAX);
> + rt_timer_queue_change(icmp_redirect_timeout_q,
> + icmp_redirtimeout);
> NET_UNLOCK();
> break;
>
> Index: netinet/ip_input.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/ip_input.c,v
> retrieving revision 1.366
> diff -u -p -r1.366 ip_input.c
> --- netinet/ip_input.c 22 Feb 2022 01:35:40 -0000 1.366
> +++ netinet/ip_input.c 19 Apr 2022 20:31:50 -0000
> @@ -91,10 +91,10 @@ int ipsendredirects = 1;
> int ip_dosourceroute = 0;
> int ip_defttl = IPDEFTTL;
> int ip_mtudisc = 1;
> -u_int ip_mtudisc_timeout = IPMTUDISCTIMEOUT;
> +int ip_mtudisc_timeout = IPMTUDISCTIMEOUT;
> int ip_directedbcast = 0;
>
> -struct rttimer_queue *ip_mtudisc_timeout_q = NULL;
> +struct rttimer_queue *ip_mtudisc_timeout_q;
>
> /* Protects `ipq' and `ip_frags'. */
> struct mutex ipq_mutex = MUTEX_INITIALIZER(IPL_SOFTNET);
> @@ -201,9 +201,7 @@ ip_init(void)
> pr->pr_protocol < IPPROTO_MAX)
> ip_protox[pr->pr_protocol] = pr - inetsw;
> LIST_INIT(&ipq);
> - if (ip_mtudisc != 0)
> - ip_mtudisc_timeout_q =
> - rt_timer_queue_create(ip_mtudisc_timeout);
> + ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout);
>
> /* Fill in list of ports not to allocate dynamically. */
> memset(&baddynamicports, 0, sizeof(baddynamicports));
> @@ -1616,24 +1614,20 @@ ip_sysctl(int *name, u_int namelen, void
> return (error);
> case IPCTL_MTUDISC:
> NET_LOCK();
> - error = sysctl_int(oldp, oldlenp, newp, newlen,
> - &ip_mtudisc);
> - if (ip_mtudisc != 0 && ip_mtudisc_timeout_q == NULL) {
> + error = sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtudisc);
> + if (ip_mtudisc == 0) {
> + rt_timer_queue_destroy(ip_mtudisc_timeout_q);
> ip_mtudisc_timeout_q =
> rt_timer_queue_create(ip_mtudisc_timeout);
> - } else if (ip_mtudisc == 0 && ip_mtudisc_timeout_q != NULL) {
> - rt_timer_queue_destroy(ip_mtudisc_timeout_q);
> - ip_mtudisc_timeout_q = NULL;
> }
> NET_UNLOCK();
> return error;
> case IPCTL_MTUDISCTIMEOUT:
> NET_LOCK();
> - error = sysctl_int(oldp, oldlenp, newp, newlen,
> - &ip_mtudisc_timeout);
> - if (ip_mtudisc_timeout_q != NULL)
> - rt_timer_queue_change(ip_mtudisc_timeout_q,
> - ip_mtudisc_timeout);
> + error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
> + &ip_mtudisc_timeout, 0, INT_MAX);
> + rt_timer_queue_change(ip_mtudisc_timeout_q,
> + ip_mtudisc_timeout);
> NET_UNLOCK();
> return (error);
> #ifdef IPSEC
> Index: netinet/ip_var.h
> ===================================================================
> RCS file: /cvs/src/sys/netinet/ip_var.h,v
> retrieving revision 1.90
> diff -u -p -r1.90 ip_var.h
> --- netinet/ip_var.h 25 Feb 2022 23:51:03 -0000 1.90
> +++ netinet/ip_var.h 19 Apr 2022 20:31:50 -0000
> @@ -204,7 +204,7 @@ extern int ip_defttl; /* default IP tt
> #define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */
>
> extern int ip_mtudisc; /* mtu discovery */
> -extern u_int ip_mtudisc_timeout; /* seconds to timeout mtu discovery */
> +extern int ip_mtudisc_timeout; /* seconds to timeout mtu
> discovery */
>
> extern int ipport_firstauto; /* min port for port allocation */
> extern int ipport_lastauto; /* max port for port allocation */
> Index: netinet6/icmp6.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/icmp6.c,v
> retrieving revision 1.238
> diff -u -p -r1.238 icmp6.c
> --- netinet6/icmp6.c 22 Feb 2022 01:15:02 -0000 1.238
> +++ netinet6/icmp6.c 19 Apr 2022 20:31:52 -0000
> @@ -118,7 +118,7 @@ struct icmp6_mtudisc_callback {
> LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks =
> LIST_HEAD_INITIALIZER(icmp6_mtudisc_callbacks);
>
> -struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
> +struct rttimer_queue *icmp6_mtudisc_timeout_q;
>
> /* XXX do these values make any sense? */
> static int icmp6_mtudisc_hiwat = 1280;
> @@ -127,7 +127,7 @@ static int icmp6_mtudisc_lowat = 256;
> /*
> * keep track of # of redirect routes.
> */
> -static struct rttimer_queue *icmp6_redirect_timeout_q = NULL;
> +struct rttimer_queue *icmp6_redirect_timeout_q;
>
> /* XXX experimental, turned off */
> static int icmp6_redirect_lowat = -1;
> @@ -1404,12 +1404,11 @@ icmp6_redirect_input(struct mbuf *m, int
> memcpy(&ssrc.sin6_addr, &src6, sizeof(struct in6_addr));
> rtredirect(sin6tosa(&sdst), sin6tosa(&sgw), sin6tosa(&ssrc),
> &newrt, m->m_pkthdr.ph_rtableid);
> -
> - if (newrt) {
> - (void)rt_timer_add(newrt, icmp6_redirect_timeout,
> + if (newrt != NULL && icmp6_redirtimeout > 0) {
> + rt_timer_add(newrt, icmp6_redirect_timeout,
> icmp6_redirect_timeout_q, m->m_pkthdr.ph_rtableid);
> - rtfree(newrt);
> }
> + rtfree(newrt);
> }
> /* finally update cached route in each socket via pfctlinput */
> {
> @@ -1881,7 +1880,6 @@ icmp6_redirect_timeout(struct rtentry *r
> }
>
> const struct sysctl_bounded_args icmpv6ctl_vars[] = {
> - { ICMPV6CTL_REDIRTIMEOUT, &icmp6_redirtimeout, 0, INT_MAX },
> { ICMPV6CTL_ND6_DELAY, &nd6_delay, 0, INT_MAX },
> { ICMPV6CTL_ND6_UMAXTRIES, &nd6_umaxtries, 0, INT_MAX },
> { ICMPV6CTL_ND6_MMAXTRIES, &nd6_mmaxtries, 0, INT_MAX },
> @@ -1916,19 +1914,30 @@ icmp6_sysctl(int *name, u_int namelen, v
>
> /* All sysctl names at this level are terminal. */
> if (namelen != 1)
> - return ENOTDIR;
> + return (ENOTDIR);
>
> switch (name[0]) {
> + case ICMPV6CTL_REDIRTIMEOUT:
> + NET_LOCK();
> + error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
> + &icmp6_redirtimeout, 0, INT_MAX);
> + rt_timer_queue_change(icmp6_redirect_timeout_q,
> + icmp6_redirtimeout);
> + NET_UNLOCK();
> + break;
>
> case ICMPV6CTL_STATS:
> - return icmp6_sysctl_icmp6stat(oldp, oldlenp, newp);
> + error = icmp6_sysctl_icmp6stat(oldp, oldlenp, newp);
> + break;
> +
> default:
> NET_LOCK();
> error = sysctl_bounded_arr(icmpv6ctl_vars,
> nitems(icmpv6ctl_vars), name, namelen, oldp, oldlenp, newp,
> newlen);
> NET_UNLOCK();
> - return (error);
> + break;
> }
> - /* NOTREACHED */
> +
> + return (error);
> }
> Index: netinet6/ip6_input.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/ip6_input.c,v
> retrieving revision 1.240
> diff -u -p -r1.240 ip6_input.c
> --- netinet6/ip6_input.c 22 Feb 2022 01:35:41 -0000 1.240
> +++ netinet6/ip6_input.c 19 Apr 2022 20:31:52 -0000
> @@ -1454,11 +1454,10 @@ ip6_sysctl(int *name, u_int namelen, voi
> #endif
> case IPV6CTL_MTUDISCTIMEOUT:
> NET_LOCK();
> - error = sysctl_int(oldp, oldlenp, newp, newlen,
> - &ip6_mtudisc_timeout);
> - if (icmp6_mtudisc_timeout_q != NULL)
> - rt_timer_queue_change(icmp6_mtudisc_timeout_q,
> - ip6_mtudisc_timeout);
> + error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
> + &ip6_mtudisc_timeout, 0, INT_MAX);
> + rt_timer_queue_change(icmp6_mtudisc_timeout_q,
> + ip6_mtudisc_timeout);
> NET_UNLOCK();
> return (error);
> case IPV6CTL_IFQUEUE:
>
--
:wq Claudio