struct tcp_conn *search_tcp_conn(unsigned int id, union sockaddr_union *to) { struct tcp_conn *conn; struct ip_addr ip; unsigned short port; unsigned int hash; port = su_getport(to); su2ip_addr( &ip, to); /* if an ID is available, search for it */ if (id!=0) { hash = tcp_hash( id, 0); lock_get( &hash_id_lock ); for( conn=hash_id_conns[hash] ; conn ; conn=conn->id_next ) { if (conn->id == id) { if (conn->state==TCP_CONN_TERM) break; /* validate the connection with ip and port! */ if ( ip_addr_cmp(&ip,&conn->rcv.src_ip) && (port==conn->rcv.src_port || port==conn->port_alias) ) { conn->ref++; lock_release( &hash_id_lock ); return conn; } /* conn id failed to match */ break; } } lock_release( &hash_id_lock ); /* conn id not found */ } /* search based on destination information (ip and port) */ hash = tcp_hash(ip.u.addr32[0],port); lock_get( &hash_ip_lock ); for( conn=hash_ip_conns[hash] ; conn ; conn=conn->ip_next ) { if ( conn->state!=TCP_CONN_TERM && ip_addr_cmp(&ip,&conn->rcv.src_ip) && (port==conn->rcv.src_port || port==conn->port_alias) ) { /* WARNING - take care, this is the only place where both * locks are taken in the same time - be aware of dead-locks! */ lock_get( &hash_id_lock ); conn->ref++; lock_release( &hash_id_lock ); lock_release( &hash_ip_lock ); return conn; } } lock_release( &hash_ip_lock ); return NULL; }
static struct tcp_sock *tcp_listen_child_sock(struct tcp_sock *tsk, struct tcp_segment *seg) { struct sock *newsk = tcp_alloc_sock(tsk->sk.protocol); struct tcp_sock *newtsk = tcpsk(newsk); tcp_set_state(newtsk, TCP_SYN_RECV); newsk->sk_saddr = seg->iphdr->ip_dst; newsk->sk_daddr = seg->iphdr->ip_src; newsk->sk_sport = seg->tcphdr->dst; newsk->sk_dport = seg->tcphdr->src; /* add to establish hash table for third ACK */ if (tcp_hash(&newtsk->sk) < 0) { free(newsk); return NULL; } /* * Why to get parent reference? * To avoid parent accidental release. * e.g: Parent is interrupted by user * when child is pending in three-way handshake. */ newtsk->parent = get_tcp_sock(tsk); /* FIXME: add limit to listen queue */ list_add(&newtsk->list, &tsk->listen_queue); /* reference for being listed into parent queue */ return get_tcp_sock(newtsk); }
static void add_tcp_hash_entry(struct tcptable *table, struct tcptableent *entry) { unsigned int hp; /* hash position in table */ struct tcp_hashentry *ptmp; hp = tcp_hash(&entry->saddr, &entry->daddr, entry->ifname); ptmp = xmallocz(sizeof(struct tcp_hashentry)); /* * Add backpointer from screen node to hash node for deletion later * (Actually point to its predecessor coz of the header cell). */ entry->hash_node = ptmp; /* * Update hash node and add it to list. */ ptmp->tcpnode = entry; ptmp->hp = hp; if (table->hash_table[hp] == NULL) { ptmp->prev_entry = NULL; table->hash_table[hp] = ptmp; ptmp->index = 1; } if (table->hash_tails[hp] != NULL) { table->hash_tails[hp]->next_entry = ptmp; ptmp->prev_entry = table->hash_tails[hp]; ptmp->index = ptmp->prev_entry->index + 1; } table->hash_tails[hp] = ptmp; ptmp->next_entry = NULL; }
void remove_tcp_conn(struct tcp_conn *conn, int extra_ref) { unsigned int h; /* unlink it asap */ lock_get( &hash_id_lock ); h = tcp_hash(conn->id,0); tcp_hash_rm_unsafe(conn,id,h); conn->ref -= extra_ref; lock_release( &hash_id_lock ); lock_get( &hash_ip_lock ); h = tcp_hash(conn->rcv.src_ip.u.addr32[0],conn->rcv.src_port); tcp_hash_rm_unsafe(conn,ip,h); lock_release( &hash_ip_lock ); /* conn is no longer in hash (cannot be found and aquired), so if ref==0 * we can safely free it */ if (conn->ref==0) free_tcp_conn(conn); }
struct tcptableent *in_table(struct tcptable *table, struct sockaddr_storage *saddr, struct sockaddr_storage *daddr, char *ifname, int logging, FILE *logfile, time_t timeout) { struct tcp_hashentry *hashptr; unsigned int hp; time_t now; if (table->head == NULL) { return 0; } /* * Determine hash table index for this set of addresses and ports */ hp = tcp_hash(saddr, daddr, ifname); hashptr = table->hash_table[hp]; while (hashptr != NULL) { if (sockaddr_is_equal(&hashptr->tcpnode->saddr, saddr) && sockaddr_is_equal(&hashptr->tcpnode->daddr, daddr) && (strcmp(hashptr->tcpnode->ifname, ifname) == 0)) break; now = time(NULL); /* * Add the timed out entries to the closed list in case we didn't * find any closed ones. */ if ((timeout > 0) && ((now - hashptr->tcpnode->lastupdate) / 60 > timeout) && (!(hashptr->tcpnode->inclosed))) { hashptr->tcpnode->timedout = 1; hashptr->tcpnode->oth_connection->timedout = 1; addtoclosedlist(table, hashptr->tcpnode); if (logging) write_timeout_log(logging, logfile, hashptr->tcpnode); } hashptr = hashptr->next_entry; } if (hashptr != NULL) { /* needed to avoid SIGSEGV */ if ((((hashptr->tcpnode->finsent == 2) && (hashptr->tcpnode->oth_connection->finsent == 2))) || (((hashptr->tcpnode->stat & FLAG_RST) || (hashptr->tcpnode->oth_connection-> stat & FLAG_RST)))) { return NULL; } else { return hashptr->tcpnode; } } else { return NULL; } }
struct tcp_conn* create_tcp_conn( int s, union sockaddr_union *src_su, struct socket_info *dst_si, tcp_conn_state init_state) { struct tcp_conn *conn; int h; if (tcp_connections_no>=tcp_max_connections) { LM_ERR("maximum number of connections exceeded: %d/%d\n", tcp_connections_no, tcp_max_connections); close(s); return NULL; } tcp_connections_no++; //make it atomic - FIXME /* allocate structure */ conn = (struct tcp_conn*)shm_malloc(sizeof(struct tcp_conn)); if (conn==NULL){ LM_ERR("no more pkg memory\n"); return NULL; } memset(conn, 0, sizeof(struct tcp_conn)); /* init lock */ if (lock_init(&conn->state_lock)==0){ LM_ERR("init lock failed\n"); goto error; } conn->state = init_state; conn->timeout = get_ticks() + tcp_lifetime; /* src info */ conn->rcv.src_su = *src_su; su2ip_addr(&conn->rcv.src_ip, src_su); conn->rcv.src_port=su_getport(src_su); /* dst info */ conn->rcv.bind_address = dst_si; conn->rcv.dst_ip = dst_si->address; conn->rcv.dst_port = dst_si->port; conn->rcv.proto = PROTO_TCP; conn->socket = s; LM_DBG("new tcp connection with: %s:%d\n", ip_addr2a(&conn->rcv.src_ip),conn->rcv.src_port); /* add to the hashes */ lock_get( &hash_id_lock ); /* refed by the hash */ conn->ref = 1; conn->id = last_tcp_id; h = tcp_hash(conn->id,0); tcp_hash_add_unsafe(conn,id,h); lock_release( &hash_id_lock ); lock_get( &hash_ip_lock ); h = tcp_hash(conn->rcv.src_ip.u.addr32[0],conn->rcv.src_port); tcp_hash_add_unsafe(conn,ip,h); lock_release( &hash_ip_lock ); return conn; error: shm_free(conn); return NULL; }
struct tcptableent *in_table(struct tcptable *table, unsigned long saddr, unsigned long daddr, unsigned int sport, unsigned int dport, char *ifname, int logging, FILE * logfile, int *nomem, struct OPTIONS *opts) { struct tcp_hashentry *hashptr; unsigned int hp; // int hastimeouts = 0; time_t now; time_t timeout; if (opts != NULL) timeout = opts->timeout; else timeout = 0; if (table->head == NULL) { return 0; } /* * Determine hash table index for this set of addresses and ports */ hp = tcp_hash(saddr, sport, daddr, dport, ifname); hashptr = table->hash_table[hp]; while (hashptr != NULL) { if ((hashptr->tcpnode->saddr.s_addr == saddr) && (hashptr->tcpnode->daddr.s_addr == daddr) && (hashptr->tcpnode->sport == sport) && (hashptr->tcpnode->dport == dport) && (strcmp(hashptr->tcpnode->ifname, ifname) == 0)) break; now = time(NULL); /* * Add the timed out entries to the closed list in case we didn't * find any closed ones. */ if ((timeout > 0) && ((now - hashptr->tcpnode->lastupdate) / 60 > timeout) && (!(hashptr->tcpnode->inclosed))) { hashptr->tcpnode->timedout = 1; hashptr->tcpnode->oth_connection->timedout = 1; addtoclosedlist(table, hashptr->tcpnode, nomem); //if (!(*nomem)) // hastimeouts = 1; if (logging) write_timeout_log(logging, logfile, hashptr->tcpnode, opts); } hashptr = hashptr->next_entry; } if (hashptr != NULL) { /* needed to avoid SIGSEGV */ if ((((hashptr->tcpnode->finsent == 2) && (hashptr->tcpnode->oth_connection->finsent == 2))) || (((hashptr->tcpnode->stat & FLAG_RST) || (hashptr->tcpnode->oth_connection->stat & FLAG_RST)))) { return NULL; } else { return hashptr->tcpnode; } } else { return NULL; } }