/* * Closes the connection held by the PCB. * Return 1 on success, 0 on error. */ int tcp_close (tcp_socket_t *s) { mutex_lock (&s->lock); tcp_debug ("tcp_close: sock $%x state=%S\n", s, tcp_state_name (s->state)); switch (s->state) { default: /* Has already been closed. */ tcp_queue_free (s); mutex_unlock (&s->lock); return 1; case LISTEN: case SYN_SENT: tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&s->ip->lock); tcp_socket_remove (s->state == LISTEN ? &s->ip->tcp_listen_sockets : &s->ip->tcp_sockets, s); mutex_unlock (&s->ip->lock); return 1; case SYN_RCVD: case ESTABLISHED: case CLOSE_WAIT: break; } for (;;) { if (tcp_enqueue_option4 (s, TCP_FIN, 0)) break; mutex_wait (&s->lock); } s->state = (s->state == CLOSE_WAIT) ? LAST_ACK : FIN_WAIT_1; if (s->unsent || (s->flags & TF_ACK_NOW)) { #if TCP_LOCK_STYLE <= TCP_LOCK_SURE mutex_unlock (&s->lock); #endif tcp_output (s); #if TCP_LOCK_STYLE <= TCP_LOCK_SURE mutex_lock (&s->lock); #endif }//if (s->unsent || (s->flags & TF_ACK_NOW)) while (s->state != CLOSED) { if (s->state == TIME_WAIT && ! s->unsent) { tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&s->ip->lock); tcp_socket_remove (&s->ip->tcp_closing_sockets, s); mutex_unlock (&s->ip->lock); return 1; } mutex_wait (&s->lock); } mutex_unlock (&s->lock); return 1; }
static inline void set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, struct tcphdr *th) { int state_idx; int new_state = IP_VS_TCP_S_CLOSE; int state_off = tcp_state_off[direction]; /* * Update state offset to INPUT_ONLY if necessary * or delete NO_OUTPUT flag if output packet detected */ if (cp->flags & IP_VS_CONN_F_NOOUTPUT) { if (state_off == TCP_DIR_OUTPUT) cp->flags &= ~IP_VS_CONN_F_NOOUTPUT; else state_off = TCP_DIR_INPUT_ONLY; } if ((state_idx = tcp_state_idx(th)) < 0) { IP_VS_DBG(8, "tcp_state_idx=%d!!!\n", state_idx); goto tcp_state_out; } new_state = pd->tcp_state_table[state_off+state_idx].next_state[cp->state]; tcp_state_out: if (new_state != cp->state) { struct ip_vs_dest *dest = cp->dest; IP_VS_DBG_BUF(8, "%s %s [%c%c%c%c] %s:%d->" "%s:%d state: %s->%s conn->refcnt:%d\n", pd->pp->name, ((state_off == TCP_DIR_OUTPUT) ? "output " : "input "), th->syn ? 'S' : '.', th->fin ? 'F' : '.', th->ack ? 'A' : '.', th->rst ? 'R' : '.', IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport), IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport), tcp_state_name(cp->state), tcp_state_name(new_state), atomic_read(&cp->refcnt)); if (dest) { if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && !tcp_state_active(new_state)) { atomic_dec(&dest->activeconns); atomic_inc(&dest->inactconns); cp->flags |= IP_VS_CONN_F_INACTIVE; } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && tcp_state_active(new_state)) { atomic_inc(&dest->activeconns); atomic_dec(&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE; } } } if (likely(pd)) cp->timeout = pd->timeout_table[cp->state = new_state]; else /* What to do ? */ cp->timeout = tcp_timeouts[cp->state = new_state]; }