From: Phil Sutter <phil@nwl.cc> Date: 2016-07-29 16:59:59
The following series contains a number of fixes necessary to make my yet
unpublished 'ss' support patch functional.
Phil Sutter (3):
sctp: Export struct sctp_info to userspace
sctp_diag: export timer value only if it is active
sctp_diag: Respect ss adding TCPF_CLOSE to idiag_states
include/linux/sctp.h | 64 -----------------------------------------------
include/uapi/linux/sctp.h | 64 +++++++++++++++++++++++++++++++++++++++++++++++
net/sctp/sctp_diag.c | 14 ++++++-----
3 files changed, 72 insertions(+), 70 deletions(-)
--
2.8.2
From: Phil Sutter <phil@nwl.cc> Date: 2016-07-29 16:59:54
Since it is exported as unsigned value, userspace has no way detecting
whether it is negative or just very large. Therefore do this in kernel
space where it is a simple comparison.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/sctp/sctp_diag.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
@@ -705,70 +705,6 @@ typedef struct sctp_auth_chunk {sctp_authhdr_tauth_hdr;}__packedsctp_auth_chunk_t;-structsctp_info{-__u32sctpi_tag;-__u32sctpi_state;-__u32sctpi_rwnd;-__u16sctpi_unackdata;-__u16sctpi_penddata;-__u16sctpi_instrms;-__u16sctpi_outstrms;-__u32sctpi_fragmentation_point;-__u32sctpi_inqueue;-__u32sctpi_outqueue;-__u32sctpi_overall_error;-__u32sctpi_max_burst;-__u32sctpi_maxseg;-__u32sctpi_peer_rwnd;-__u32sctpi_peer_tag;-__u8sctpi_peer_capable;-__u8sctpi_peer_sack;-__u16__reserved1;--/* assoc status info */-__u64sctpi_isacks;-__u64sctpi_osacks;-__u64sctpi_opackets;-__u64sctpi_ipackets;-__u64sctpi_rtxchunks;-__u64sctpi_outofseqtsns;-__u64sctpi_idupchunks;-__u64sctpi_gapcnt;-__u64sctpi_ouodchunks;-__u64sctpi_iuodchunks;-__u64sctpi_oodchunks;-__u64sctpi_iodchunks;-__u64sctpi_octrlchunks;-__u64sctpi_ictrlchunks;--/* primary transport info */-structsockaddr_storagesctpi_p_address;-__s32sctpi_p_state;-__u32sctpi_p_cwnd;-__u32sctpi_p_srtt;-__u32sctpi_p_rto;-__u32sctpi_p_hbinterval;-__u32sctpi_p_pathmaxrxt;-__u32sctpi_p_sackdelay;-__u32sctpi_p_sackfreq;-__u32sctpi_p_ssthresh;-__u32sctpi_p_partial_bytes_acked;-__u32sctpi_p_flight_size;-__u16sctpi_p_error;-__u16__reserved2;--/* sctp sock info */-__u32sctpi_s_autoclose;-__u32sctpi_s_adaptation_ind;-__u32sctpi_s_pd_point;-__u8sctpi_s_nodelay;-__u8sctpi_s_disable_fragments;-__u8sctpi_s_v4mapped;-__u8sctpi_s_frag_interleave;-__u32sctpi_s_type;-__u32__reserved3;-};-structsctp_infox{structsctp_info*sctpinfo;structsctp_association*asoc;
@@ -944,4 +944,68 @@ struct sctp_default_prinfo {__u16pr_policy;};+structsctp_info{+__u32sctpi_tag;+__u32sctpi_state;+__u32sctpi_rwnd;+__u16sctpi_unackdata;+__u16sctpi_penddata;+__u16sctpi_instrms;+__u16sctpi_outstrms;+__u32sctpi_fragmentation_point;+__u32sctpi_inqueue;+__u32sctpi_outqueue;+__u32sctpi_overall_error;+__u32sctpi_max_burst;+__u32sctpi_maxseg;+__u32sctpi_peer_rwnd;+__u32sctpi_peer_tag;+__u8sctpi_peer_capable;+__u8sctpi_peer_sack;+__u16__reserved1;++/* assoc status info */+__u64sctpi_isacks;+__u64sctpi_osacks;+__u64sctpi_opackets;+__u64sctpi_ipackets;+__u64sctpi_rtxchunks;+__u64sctpi_outofseqtsns;+__u64sctpi_idupchunks;+__u64sctpi_gapcnt;+__u64sctpi_ouodchunks;+__u64sctpi_iuodchunks;+__u64sctpi_oodchunks;+__u64sctpi_iodchunks;+__u64sctpi_octrlchunks;+__u64sctpi_ictrlchunks;++/* primary transport info */+structsockaddr_storagesctpi_p_address;+__s32sctpi_p_state;+__u32sctpi_p_cwnd;+__u32sctpi_p_srtt;+__u32sctpi_p_rto;+__u32sctpi_p_hbinterval;+__u32sctpi_p_pathmaxrxt;+__u32sctpi_p_sackdelay;+__u32sctpi_p_sackfreq;+__u32sctpi_p_ssthresh;+__u32sctpi_p_partial_bytes_acked;+__u32sctpi_p_flight_size;+__u16sctpi_p_error;+__u16__reserved2;++/* sctp sock info */+__u32sctpi_s_autoclose;+__u32sctpi_s_adaptation_ind;+__u32sctpi_s_pd_point;+__u8sctpi_s_nodelay;+__u8sctpi_s_disable_fragments;+__u8sctpi_s_v4mapped;+__u8sctpi_s_frag_interleave;+__u32sctpi_s_type;+__u32__reserved3;+};+#endif /* _UAPI_SCTP_H */
From: Phil Sutter <phil@nwl.cc> Date: 2016-07-29 17:00:12
Since 'ss' always adds TCPF_CLOSE to idiag_states flags, sctp_diag can't
rely upon TCPF_LISTEN flag solely being present when listening sockets
are requested.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/sctp/sctp_diag.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
On Fri, Jul 29, 2016 at 06:59:39PM +0200, Phil Sutter wrote:
quoted hunk
Since it is exported as unsigned value, userspace has no way detecting
whether it is negative or just very large. Therefore do this in kernel
space where it is a simple comparison.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/sctp/sctp_diag.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
I think we have two issues here, prior to your patch, but I noticed
while reviewing it :-)
This array is actually not based on jiffies but on intervals instead, as
per:
sm_sideeffect.c:
case SCTP_CMD_TIMER_START: [1]
timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[cmd->obj.to]; <---
BUG_ON(!timeout);
timer->expires = jiffies + timeout; <---
But more importantly, this array is actually not used for this timeout
and the timeout is sctp_transport dependant, as per:
/* Schedule retransmission on the given transport */
void sctp_transport_immediate_rtx(struct sctp_transport *t)
{
/* Stop pending T3_rtx_timer */
if (del_timer(&t->T3_rtx_timer))
sctp_transport_put(t);
sctp_retransmit(&t->asoc->outqueue, t, SCTP_RTXR_T3_RTX);
if (!timer_pending(&t->T3_rtx_timer)) {
if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto))
^^^^^^^^^^^^^^^^
sctp_transport_hold(t);
Note how on sctp_get_sctp_info() it fetches the RTO (which is T3_RTX)
this way:
info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
If we want to know how long is left for the timer to expire, we have to
read directly from it.
With git grep -A 1 TIMER_START we can confirm that [1] is never hit for
SCTP_EVENT_TIMEOUT_T3_RTX. Yet, the asoc is allocated with kzalloc(), so
I guess you were just reading -jiffies in there.
Note however that the stats rtx_data_chunks is the accumulated stats,
it's good, and that we may have multiple T3 timers running at once, with
different timeouts.
Xin, ideas on how we can fix this? I'm not sure if we can dump
per-transport info in there. Not as it is now, I guess.
I think we have two issues here, prior to your patch, but I noticed
while reviewing it :-)
This array is actually not based on jiffies but on intervals instead, as
per:
sm_sideeffect.c:
case SCTP_CMD_TIMER_START: [1]
timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[cmd->obj.to]; <---
BUG_ON(!timeout);
timer->expires = jiffies + timeout; <---
understood.
But more importantly, this array is actually not used for this timeout
and the timeout is sctp_transport dependant, as per:
/* Schedule retransmission on the given transport */
void sctp_transport_immediate_rtx(struct sctp_transport *t)
{
/* Stop pending T3_rtx_timer */
if (del_timer(&t->T3_rtx_timer))
sctp_transport_put(t);
sctp_retransmit(&t->asoc->outqueue, t, SCTP_RTXR_T3_RTX);
if (!timer_pending(&t->T3_rtx_timer)) {
if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto))
^^^^^^^^^^^^^^^^
sctp_transport_hold(t);
Note how on sctp_get_sctp_info() it fetches the RTO (which is T3_RTX)
this way:
info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
If we want to know how long is left for the timer to expire, we have to
read directly from it.
you are right, 3 timers (T3_tx, hb, rtx_data_chunks) are per transport.
With git grep -A 1 TIMER_START we can confirm that [1] is never hit for
SCTP_EVENT_TIMEOUT_T3_RTX. Yet, the asoc is allocated with kzalloc(), so
I guess you were just reading -jiffies in there.
Note however that the stats rtx_data_chunks is the accumulated stats,
it's good, and that we may have multiple T3 timers running at once, with
different timeouts.
Xin, ideas on how we can fix this? I'm not sure if we can dump
per-transport info in there. Not as it is now, I guess.
It's not that easy to dump all transports info besed on current sctp_diag codes.
Now for the transport's info, we only choose primary_path to dump.
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
what do you think ?
I think we have two issues here, prior to your patch, but I noticed
while reviewing it :-)
This array is actually not based on jiffies but on intervals instead, as
per:
sm_sideeffect.c:
case SCTP_CMD_TIMER_START: [1]
timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[cmd->obj.to]; <---
BUG_ON(!timeout);
timer->expires = jiffies + timeout; <---
understood.
quoted
But more importantly, this array is actually not used for this timeout
and the timeout is sctp_transport dependant, as per:
/* Schedule retransmission on the given transport */
void sctp_transport_immediate_rtx(struct sctp_transport *t)
{
/* Stop pending T3_rtx_timer */
if (del_timer(&t->T3_rtx_timer))
sctp_transport_put(t);
sctp_retransmit(&t->asoc->outqueue, t, SCTP_RTXR_T3_RTX);
if (!timer_pending(&t->T3_rtx_timer)) {
if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto))
^^^^^^^^^^^^^^^^
sctp_transport_hold(t);
Note how on sctp_get_sctp_info() it fetches the RTO (which is T3_RTX)
this way:
info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
If we want to know how long is left for the timer to expire, we have to
read directly from it.
you are right, 3 timers (T3_tx, hb, rtx_data_chunks) are per transport.
quoted
With git grep -A 1 TIMER_START we can confirm that [1] is never hit for
SCTP_EVENT_TIMEOUT_T3_RTX. Yet, the asoc is allocated with kzalloc(), so
I guess you were just reading -jiffies in there.
Note however that the stats rtx_data_chunks is the accumulated stats,
it's good, and that we may have multiple T3 timers running at once, with
different timeouts.
Xin, ideas on how we can fix this? I'm not sure if we can dump
per-transport info in there. Not as it is now, I guess.
It's not that easy to dump all transports info besed on current sctp_diag codes.
Okay
Now for the transport's info, we only choose primary_path to dump.
Okay
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
Yes :)
what do you think ?
Makes sense, LGTM.
Phil, not sure how you want to proceed here. Wanna handle the change above?
I think we have two issues here, prior to your patch, but I noticed
while reviewing it :-)
This array is actually not based on jiffies but on intervals instead, as
per:
sm_sideeffect.c:
case SCTP_CMD_TIMER_START: [1]
timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[cmd->obj.to]; <---
BUG_ON(!timeout);
timer->expires = jiffies + timeout; <---
understood.
quoted
But more importantly, this array is actually not used for this timeout
and the timeout is sctp_transport dependant, as per:
/* Schedule retransmission on the given transport */
void sctp_transport_immediate_rtx(struct sctp_transport *t)
{
/* Stop pending T3_rtx_timer */
if (del_timer(&t->T3_rtx_timer))
sctp_transport_put(t);
sctp_retransmit(&t->asoc->outqueue, t, SCTP_RTXR_T3_RTX);
if (!timer_pending(&t->T3_rtx_timer)) {
if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto))
^^^^^^^^^^^^^^^^
sctp_transport_hold(t);
Note how on sctp_get_sctp_info() it fetches the RTO (which is T3_RTX)
this way:
info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
If we want to know how long is left for the timer to expire, we have to
read directly from it.
you are right, 3 timers (T3_tx, hb, rtx_data_chunks) are per transport.
quoted
With git grep -A 1 TIMER_START we can confirm that [1] is never hit for
SCTP_EVENT_TIMEOUT_T3_RTX. Yet, the asoc is allocated with kzalloc(), so
I guess you were just reading -jiffies in there.
Note however that the stats rtx_data_chunks is the accumulated stats,
it's good, and that we may have multiple T3 timers running at once, with
different timeouts.
Xin, ideas on how we can fix this? I'm not sure if we can dump
per-transport info in there. Not as it is now, I guess.
It's not that easy to dump all transports info besed on current sctp_diag codes.
Okay
quoted
Now for the transport's info, we only choose primary_path to dump.
Okay
quoted
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
Yes :)
quoted
what do you think ?
Makes sense, LGTM.
Phil, not sure how you want to proceed here. Wanna handle the change above?
I'll look into this next week. One early question: Does the above mean
we are printing the primary path's timer value for every assoc? If so,
shouldn't we do that for just the EP or the primary path's assoc even?
Thanks, Phil
From: Xin Long <lucien.xin@gmail.com> Date: 2016-07-31 15:57:05
I'll look into this next week. One early question: Does the above mean
we are printing the primary path's timer value for every assoc? If so,
shouldn't we do that for just the EP or the primary path's assoc even?
Nope, we can't say "the primary path's assoc".
Every assoc has their own primary path, even these assocs belong
to one EP. you can see it from (asoc->peer.primary_path).
From: Phil Sutter <phil@nwl.cc> Date: 2016-08-03 19:28:30
Hi,
On Sat, Jul 30, 2016 at 09:25:42PM +0800, Xin Long wrote:
[...]
Now for the transport's info, we only choose primary_path to dump.
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
I have changed the code to this:
| struct timer_list *t3_rtx = &asoc->peer.primary_path->T3_rtx_timer;
|
| [...]
|
| if (timer_pending(t3_rtx)) {
| r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX;
| r->idiag_retrans = asoc->rtx_data_chunks;
| r->idiag_expires = jiffies_to_msecs(t3_rtx->expires - jiffies);
| }
And I'm still getting what appears to be negative values sometimes. Here
are some of the common values in hex when busy looping sctp_diag
requests:
0
7530
1000000
3000000
6000000
14000000
94000000
ed690000
ffffea00
While I wonder a bit about the zero, the last two seem to be unsigned
underruns. Do I still have to check for 't3_rtx->expires > jiffies' or
am I missing something?
Thanks, Phil
On Wed, Aug 03, 2016 at 09:28:13PM +0200, Phil Sutter wrote:
Hi,
On Sat, Jul 30, 2016 at 09:25:42PM +0800, Xin Long wrote:
[...]
quoted
Now for the transport's info, we only choose primary_path to dump.
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
I have changed the code to this:
| struct timer_list *t3_rtx = &asoc->peer.primary_path->T3_rtx_timer;
|
| [...]
|
| if (timer_pending(t3_rtx)) {
| r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX;
| r->idiag_retrans = asoc->rtx_data_chunks;
| r->idiag_expires = jiffies_to_msecs(t3_rtx->expires - jiffies);
| }
And I'm still getting what appears to be negative values sometimes. Here
are some of the common values in hex when busy looping sctp_diag
requests:
0
7530
1000000
3000000
6000000
14000000
94000000
ed690000
ffffea00
Are these for the same asoc? I wouldn't expect it to vary that much.
Even the 1000000 it's already just too big to be reasonable. That's
16777 seconds. Only 0x7530 is reasonable, 30 seconds.
While I wonder a bit about the zero, the last two seem to be unsigned
underruns. Do I still have to check for 't3_rtx->expires > jiffies' or
am I missing something?
You shouldn't have to because then the timer wouldn't be pending.
I don't know what can be wrong in there. Could it be the application not
checking if the timer was exported or not before dumping it? </longshot>
Marcelo
From: Phil Sutter <phil@nwl.cc> Date: 2016-08-03 20:24:25
On Wed, Aug 03, 2016 at 04:46:52PM -0300, Marcelo Ricardo Leitner wrote:
On Wed, Aug 03, 2016 at 09:28:13PM +0200, Phil Sutter wrote:
quoted
Hi,
On Sat, Jul 30, 2016 at 09:25:42PM +0800, Xin Long wrote:
[...]
quoted
Now for the transport's info, we only choose primary_path to dump.
It means we should fix this by getting the left time to expire from
primary transport t->T3_rtx_timer. like:
r->idiag_expires = jiffies_to_msecs(
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
+ asoc->peer.primary_path->T3_rtx_timer.expires - jiffies);
but yes, need to check with timer_pending firstly.
I have changed the code to this:
| struct timer_list *t3_rtx = &asoc->peer.primary_path->T3_rtx_timer;
|
| [...]
|
| if (timer_pending(t3_rtx)) {
| r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX;
| r->idiag_retrans = asoc->rtx_data_chunks;
| r->idiag_expires = jiffies_to_msecs(t3_rtx->expires - jiffies);
| }
And I'm still getting what appears to be negative values sometimes. Here
are some of the common values in hex when busy looping sctp_diag
requests:
0
7530
1000000
3000000
6000000
14000000
94000000
ed690000
ffffea00
Are these for the same asoc? I wouldn't expect it to vary that much.
Even the 1000000 it's already just too big to be reasonable. That's
16777 seconds. Only 0x7530 is reasonable, 30 seconds.
Nope, those are not for the same asoc. Also, I piped the gathered values
through 'sort -u', so they are not in chronological order.
quoted
While I wonder a bit about the zero, the last two seem to be unsigned
underruns. Do I still have to check for 't3_rtx->expires > jiffies' or
am I missing something?
You shouldn't have to because then the timer wouldn't be pending.
I don't know what can be wrong in there. Could it be the application not
checking if the timer was exported or not before dumping it? </longshot>
Nope, the application is 'ss' in that case (with added debug output to
get the raw values), but your shot wasn't that long after all: I
discovered that in sctp_diag.ko, the inet_diag_msg object to be sent to
userspace is not cleared initially. So by populating r->idiag_timer
conditionally, I managed to leak random data to user space. DOH!
Thanks for the pointer,
Phil