Bug 1493210 - xinetd does not handle adding or removing KEEPALIVE flag correctly on reload
Summary: xinetd does not handle adding or removing KEEPALIVE flag correctly on reload
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Red Hat Enterprise Linux 6
Classification: Red Hat
Component: xinetd
Version: 6.9
Hardware: Unspecified
OS: Unspecified
unspecified
low
Target Milestone: rc
: ---
Assignee: Jan Synacek
QA Contact: qe-baseos-daemons
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2017-09-19 15:18 UTC by Dan Astoorian
Modified: 2017-09-25 06:18 UTC (History)
0 users

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-09-25 06:18:45 UTC
Target Upstream Version:


Attachments (Terms of Use)

Description Dan Astoorian 2017-09-19 15:18:08 UTC
Description of problem:
After adding or removing "flags = KEEPALIVE" from a service and doing "service xinetd reload", the SO_KEEPALIVE option is not updated correctly.

Version-Release number of selected component (if applicable):
xinetd-2.3.14-40.el6.x86_64

How reproducible:
Always

Steps to Reproduce:
1. Change "disable = yes" to "disable = no" in /etc/xinetd.d/echo-stream
2. Start the xinetd service:
    # service xinetd restart
3. Add "flags = keepalive" to /etc/xinetd.d/echo-stream
4. Reload xinetd:
    # service xinetd reload
5. Establish a connection to the echo service:
    # nc localhost 7
6. Observe that the keepalive option is not set for this first connection:
    # netstat -to | grep 'echo.*ESTABLISHED'
7. Ctrl-C out of the nc command
8. Repeat steps 5 through 7; note that keepalive is set on the server side for the second and subsequent connections.
9. Remove "flags = keepalive" from /etc/xinetd.d/echo-stream
10. Reload xinetd again:
    # service xinetd reload
11. Establish another connection to the echo service:
    # nc localhost 7
12. Observe that the keepalive option is still enabled:
    # netstat -to | grep 'echo.*ESTABLISHED'

Actual results:
In step 6 (first time):
tcp        0      0 localhost:echo              localhost:39466             ESTABLISHED off (0.00/0/0)
tcp        0      0 localhost:39466             localhost:echo              ESTABLISHED off (0.00/0/0)

In step 12:
tcp        0      0 localhost:39480             localhost:echo              ESTABLISHED off (0.00/0/0)
tcp        0      0 localhost:echo              localhost:39480             ESTABLISHED keepalive (7197.50/0/0)


Expected results:
There should be a keepalive timer listed for one the server-side connection displayed in step 6, and not in step 12.

Additional info:
Note that only the first connection after adding flags=KEEPALIVE is missing the keepalive flag; however, once it is set, it is never removed after removing flags=KEEPALIVE.

In xinetd/connection.c, the setsockopt() call to turn on SO_KEEPALIVE is done on the socket which is being listened on, and not the socket that was accept()'d; the new connection never gets SO_KEEPALIVE applied to it.  The same error appears to apply to the NODELAY flag and the TCP_NODELAY option, but I have not tested whether the same bug applies to it.

Note that moving the setsockopt() calls before the accept() does not appear to fix the problem: the code should not rely on flags like SO_KEEPALIVE being inherited from the listening socket.  (I suspect that the option is only inherited if it was in effect at the time the connection was established by the kernel, and not the time the application calls accept().  In any case, it is not portable to set SO_KEEPALIVE on the listening socket and rely on it being inherited by the socket returned by accept().) 

I don't see any code at all that ever attempts to do setsockopt(..., SO_KEEPALIVE, ...) with a value of zero to try to turn the option off.  Again, it looks as if the same flaw may apply to the NODELAY flag, but I have not tested this.

Perhaps the simplest fix would be to change:

      if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) )
      {
         if( setsockopt(SVC_FD(sp), SOL_SOCKET, SO_KEEPALIVE,
                        (char *)&on, sizeof( on ) ) < 0 )
            ...

to something more like:

      if( (SC_PROTOVAL( scp ) == IPPROTO_TCP) ) {
          if (SC_KEEPALIVE( scp ) )
               on = 1;
          else
               on = 0;
          if( setsockopt(cp->co_descriptor, SOL_SOCKET, SO_KEEPALIVE,
                        (char *)&on, sizeof( on ) ) < 0 )
            ...

with perhaps a corresponding change for TCP_NODELAY.

With this correction, the code in xinetd/service.c to set the option on the listening socket could probably be removed.

Comment 2 Jan Synacek 2017-09-25 06:18:45 UTC
Red Hat Enterprise Linux 6 is in the Production 3 Phase. During the
Production 3 Phase, Critical impact Security Advisories (RHSAs) and
selected Urgent Priority Bug Fix Advisories (RHBAs) may be released as
they become available.

The official life cycle policy can be reviewed here:

http://redhat.com/rhel/lifecycle

This issue does not meet the inclusion criteria for the Production 3 Phase
and will be marked as CLOSED/WONTFIX. If this remains a critical
requirement, please contact Red Hat Customer Support to request
a re-evaluation of the issue, citing a clear business justification. Note
that a strong business justification will be required for re-evaluation.
Red Hat Customer Support can be contacted via the Red Hat Customer Portal
at the following URL:

https://access.redhat.com/


Note You need to log in before you can comment on or make changes to this bug.