mirror of
https://github.com/kamailio/kamailio
synced 2026-08-07 16:32:08 -04:00
core: tcp - fix close events missing or wrong reason
- audit all sites for TCP close: TCP events missing or wrong reason so that tcpops will run the correct event route: tcp:closed / tcp:timeout / tcp:reset
This commit is contained in:
parent
ba732b2340
commit
8bfb74bb10
2 changed files with 54 additions and 7 deletions
|
|
@ -4124,6 +4124,9 @@ inline static int handle_tcp_child(struct tcp_child *tcp_c, int fd_i)
|
|||
#endif /* TCP_ASYNC */
|
||||
break;
|
||||
}
|
||||
/* the connection is returning to service:
|
||||
* clear any stale close reason a prior timeout left on it. */
|
||||
tcpconn->event = 0;
|
||||
/* update the timeout*/
|
||||
t = get_ticks_raw();
|
||||
con_lifetime = tcpconn->lifetime;
|
||||
|
|
@ -4359,6 +4362,8 @@ inline static int handle_ser_child(struct process_table *p, int fd_i)
|
|||
io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);
|
||||
tcpconn->flags &= ~(F_CONN_WRITE_W | F_CONN_READ_W);
|
||||
}
|
||||
/* emit the close event route (tcp:closed/timeout/reset) */
|
||||
tcp_emit_closed_event(tcpconn);
|
||||
tcpconn_put_destroy(tcpconn); /* dec refcnt & destroy on 0 */
|
||||
break;
|
||||
case CONN_GET_FD:
|
||||
|
|
@ -5157,6 +5162,11 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln *tl, void *data)
|
|||
TCP_STATS_CON_TIMEOUT();
|
||||
#endif /* TCP_ASYNC */
|
||||
LM_DBG("timeout for %p\n", c);
|
||||
/* The connection timed out and is torn down here.
|
||||
* Emit the close event so the tcp:timeout event route fires */
|
||||
if(c->event == 0)
|
||||
c->event = TCP_CLOSED_TIMEOUT;
|
||||
tcp_emit_closed_event(c);
|
||||
if(likely(c->flags & F_CONN_HASHED)) {
|
||||
c->flags &= ~(F_CONN_HASHED | F_CONN_MAIN_TIMER);
|
||||
c->state = S_CONN_BAD;
|
||||
|
|
@ -5893,6 +5903,9 @@ void tcp_timer_check_connections(unsigned int ticks, void *param)
|
|||
mcmd[0] = (long)con;
|
||||
mcmd[1] = CONN_EOF;
|
||||
|
||||
/* message read/data timeout: record the close reason
|
||||
* so that tcpops can identify tcp:timeout */
|
||||
con->event = TCP_CLOSED_TIMEOUT;
|
||||
con->send_flags.f |= SND_F_CON_CLOSE;
|
||||
con->flags |= F_CONN_FORCE_EOF;
|
||||
|
||||
|
|
|
|||
|
|
@ -313,9 +313,14 @@ int tcp_read_data(int fd, struct tcp_connection *c, char *buf, int b_size,
|
|||
rd_conn_flags_t *flags)
|
||||
{
|
||||
int bytes_read;
|
||||
int read_errno = 0; /* errno captured at the read() syscall (see below) */
|
||||
|
||||
again:
|
||||
bytes_read = read(fd, buf, b_size);
|
||||
/* Snapshot errno immediately: switch()/dst_blocklist and
|
||||
* LOG()/async_tkv_emit() may change errno.
|
||||
* Needed to set the correct close reason for tcpops */
|
||||
read_errno = errno;
|
||||
|
||||
if(likely(bytes_read != b_size)) {
|
||||
if(unlikely(bytes_read == -1)) {
|
||||
|
|
@ -366,16 +371,30 @@ again:
|
|||
}
|
||||
LOG(cfg_get(core, core_cfg, corelog),
|
||||
"error reading: %s (%d) ([%s]:%u -> [%s]:%u)\n",
|
||||
strerror(errno), errno, ip_addr2xa(&c->rcv.src_ip),
|
||||
c->rcv.src_port, ip_addr2xa(&c->rcv.dst_ip),
|
||||
c->rcv.dst_port);
|
||||
strerror(read_errno), read_errno,
|
||||
ip_addr2xa(&c->rcv.src_ip), c->rcv.src_port,
|
||||
ip_addr2xa(&c->rcv.dst_ip), c->rcv.dst_port);
|
||||
async_tkv_emit(1200, "tcp-read-error",
|
||||
"erno=%d;srcip=%s;dstip=%s", errno,
|
||||
"erno=%d;srcip=%s;dstip=%s", read_errno,
|
||||
ip_addr2xa(&c->rcv.src_ip), ip_addr2xa(&c->rcv.dst_ip));
|
||||
if(errno == ETIMEDOUT) {
|
||||
/* classify using read_errno */
|
||||
if(read_errno == ETIMEDOUT) {
|
||||
c->event = TCP_CLOSED_TIMEOUT;
|
||||
} else if(errno == ECONNRESET) {
|
||||
} else if(read_errno == ECONNRESET) {
|
||||
c->event = TCP_CLOSED_RESET;
|
||||
} else {
|
||||
/* read_errno itself unhelpful (e.g. a TLS-layer failure that
|
||||
* left no socket errno) - recover the true socket error from
|
||||
* SO_ERROR so a reset is still classified as such. */
|
||||
int soerr = 0;
|
||||
socklen_t soerr_len = sizeof(soerr);
|
||||
int grc = getsockopt(
|
||||
fd, SOL_SOCKET, SO_ERROR, &soerr, &soerr_len);
|
||||
if(grc == 0 && soerr == ECONNRESET) {
|
||||
c->event = TCP_CLOSED_RESET;
|
||||
} else if(grc == 0 && soerr == ETIMEDOUT) {
|
||||
c->event = TCP_CLOSED_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -387,7 +406,22 @@ again:
|
|||
ip_addr2xa(&c->rcv.dst_ip), c->rcv.dst_port);
|
||||
c->state = S_CONN_EOF;
|
||||
*flags |= RD_CONN_EOF;
|
||||
c->event = TCP_CLOSED_EOF;
|
||||
/* Classify the close reason - otherwise
|
||||
* tcp:reset may be mis-identified as tcp:closed
|
||||
* and tcpops runs the wrong route. */
|
||||
if(likely(c->event == 0)) {
|
||||
int soerr = 0;
|
||||
socklen_t soerr_len = sizeof(soerr);
|
||||
if(unlikely(bytes_read != 0)
|
||||
&& getsockopt(
|
||||
fd, SOL_SOCKET, SO_ERROR, &soerr, &soerr_len)
|
||||
== 0
|
||||
&& soerr == ECONNRESET) {
|
||||
c->event = TCP_CLOSED_RESET;
|
||||
} else {
|
||||
c->event = TCP_CLOSED_EOF;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(unlikely(c->state == S_CONN_CONNECT
|
||||
|| c->state == S_CONN_ACCEPT)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue