/* takes socket at state CLOSED, and start connect to s->remote * if s not CLOSED, return null * \return socket in connection state * \return = SExxx - some error * * */ tcp_socket_t *tcp_connect_restart (tcp_socket_t *s) { unsigned long optdata; ip_t *ip = s->ip; tcp_debug ("tcp reconnect to port %u\n", s->remote_port); s->lastack = s->snd_nxt - 1; s->snd_lbb = s->snd_nxt - 1; s->snd_wnd = TCP_WND; s->ssthresh = s->mss * 10; s->state = SYN_SENT; /* Build an MSS option */ optdata = HTONL (((unsigned long)2 << 24) | ((unsigned long)4 << 16) | (((unsigned long)s->mss / 256) << 8) | (s->mss & 255)); if (! tcp_enqueue_option4 (s, TCP_SYN, optdata)) { return 0; } mutex_lock (&ip->lock); s->tmr = ip->tcp_ticks; tcp_list_add (&ip->tcp_sockets, s); #if TCP_LOCK_STYLE <= TCP_LOCK_SURE tcp_output (s); mutex_unlock (&ip->lock); #elif TCP_LOCK_STYLE >= TCP_LOCK_RELAXED mutex_unlock (&ip->lock); tcp_output (s); #endif mutex_unlock (&s->lock); return s; }
/* * Set the state of the connection to be LISTEN, which means that it * is able to accept incoming connections. The protocol control block * is reallocated in order to consume less memory. Setting the * connection to LISTEN is an irreversible process. */ tcp_socket_t *tcp_listen (ip_t *ip, unsigned char *ipaddr, unsigned short port) { tcp_socket_t *s, *cs; ip_addr targetip; if (ipaddr) { targetip = ipadr_4ucs(ipaddr); } else targetip.val = 0; s = tcp_alloc (ip); if (s == 0) { return 0; } /* Bind the connection to a local portnumber and IP address. */ mutex_lock (&ip->lock); if (port == 0) { port = tcp_new_port (ip); } tcp_debug ("tcp_listen: port %u\n", port); /* Check if the address already is in use. */ for (cs = ip->tcp_listen_sockets; cs != 0; cs = cs->next) { if (cs->local_port == port) { if ( ipadr_is_same(targetip.var, cs->local_ip) ) { mutex_unlock (&ip->lock); mem_free (s); return 0; } } } for (cs = ip->tcp_sockets; cs != 0; cs = cs->next) { if (cs->local_port == port) { if (ipadr_is_same(targetip.var, cs->local_ip) ) { mutex_unlock (&ip->lock); mem_free (s); return 0; } } } if ( ipadr_not0(targetip.var) ) { s->local_ip = targetip.var; } s->local_port = port; s->state = LISTEN; buf_queueh_init(&s->inq, sizeof(s->queue)); tcp_list_add (&ip->tcp_listen_sockets, s); mutex_unlock (&ip->lock); return s; }
/* * Set the state of the connection to be LISTEN, which means that it * is able to accept incoming connections. The protocol control block * is reallocated in order to consume less memory. Setting the * connection to LISTEN is an irreversible process. */ tcp_socket_t *tcp_listen (ip_t *ip, unsigned char *ipaddr, unsigned short port) { tcp_socket_t *s, *cs; if (! ipaddr) { ipaddr = (unsigned char*) "\0\0\0\0"; } s = mem_alloc (ip->pool, sizeof (tcp_socket_t)); if (s == 0) { return 0; } s->ip = ip; /* Bind the connection to a local portnumber and IP address. */ mutex_lock (&ip->lock); if (port == 0) { port = tcp_new_port (ip); } tcp_debug ("tcp_listen: port %u\n", port); /* Check if the address already is in use. */ for (cs = ip->tcp_listen_sockets; cs != 0; cs = cs->next) { if (cs->local_port == port) { if (memcmp (cs->local_ip, IP_ADDR(0), 4) == 0 || memcmp (ipaddr, IP_ADDR(0), 4) == 0 || memcmp (cs->local_ip, ipaddr, 4) == 0) { mutex_unlock (&ip->lock); mem_free (s); return 0; } } } for (cs = ip->tcp_sockets; cs != 0; cs = cs->next) { if (cs->local_port == port) { if (memcmp (cs->local_ip, IP_ADDR(0), 4) == 0 || memcmp (ipaddr, IP_ADDR(0), 4) == 0 || memcmp (cs->local_ip, ipaddr, 4) == 0) { mutex_unlock (&ip->lock); mem_free (s); return 0; } } } if (memcmp (ipaddr, IP_ADDR(0), 4) != 0) { memcpy (s->local_ip, ipaddr, 4); } s->local_port = port; s->state = LISTEN; tcp_list_add (&ip->tcp_listen_sockets, s); mutex_unlock (&ip->lock); return s; }
/* * Connect to another host. Wait until connection established. * Return 1 on success, 0 on error. */ tcp_socket_t * tcp_connect (ip_t *ip, unsigned char *ipaddr, unsigned short port) { tcp_socket_t *s; unsigned long optdata; tcp_debug ("tcp_connect to port %u\n", port); if (ipaddr == 0) return 0; mutex_lock (&ip->lock); s = tcp_alloc (ip); memcpy (s->remote_ip, ipaddr, 4); s->remote_port = port; if (s->local_port == 0) { s->local_port = tcp_new_port (ip); } s->lastack = s->snd_nxt - 1; s->snd_lbb = s->snd_nxt - 1; s->snd_wnd = TCP_WND; s->ssthresh = s->mss * 10; s->state = SYN_SENT; /* Build an MSS option */ optdata = HTONL (((unsigned long)2 << 24) | ((unsigned long)4 << 16) | (((unsigned long)s->mss / 256) << 8) | (s->mss & 255)); if (! tcp_enqueue (s, 0, 0, TCP_SYN, (unsigned char*) &optdata, 4)) { mem_free (s); mutex_unlock (&ip->lock); return 0; } tcp_list_add (&ip->tcp_sockets, s); tcp_output (s); mutex_unlock (&ip->lock); mutex_lock (&s->lock); for (;;) { mutex_wait (&s->lock); if (s->state == ESTABLISHED) { mutex_unlock (&s->lock); return s; } if (s->state == CLOSED) { mutex_unlock (&s->lock); mem_free (s); return 0; } } }
tcp_socket_t *tcp_accept_until (tcp_socket_t *s , scheduless_condition waitfor, void* waitarg) { tcp_socket_t *ns; buf_t *p; ip_hdr_t *iph; tcp_hdr_t *h; unsigned long optdata; int ok = tcp_lock_avail(s, (1<<LISTEN) , waitfor, waitarg); if (ok != 0){ if (s->state != LISTEN){ tcp_debug ("tcp_accept: called in invalid state\n"); return (tcp_socket_t *)SEINVAL; } if (ok == -1) return 0; return (tcp_socket_t *)ok; } /* Create a new PCB, and respond with a SYN|ACK. * If a new PCB could not be created (probably due to lack of memory), * we don't do anything, but rely on the sender will retransmit * the SYN at a time when we have more memory available. */ mutex_lock (&s->ip->lock); ns = tcp_alloc (s->ip); if (ns == 0) { mutex_unlock (&s->lock); ++(s->ip->tcp_in_discards); mutex_unlock (&s->ip->lock); tcp_debug ("tcp_accept: could not allocate PCB\n"); return (tcp_socket_t *)SENOMEM; } p = tcp_queue_get (s); h = (tcp_hdr_t*) p->payload; iph = ((ip_hdr_t*) p->payload) - 1; /* Set up the new PCB. */ ns->local_ip = iph->dest.var; ns->local_port = s->local_port; ns->remote_ip = iph->src; ns->remote_port = h->src; ns->state = SYN_RCVD; ns->rcv_nxt = s->ip->tcp_input_seqno + 1; ns->snd_wnd = h->wnd; ns->ssthresh = ns->snd_wnd; ns->snd_wl1 = s->ip->tcp_input_seqno; ns->ip = s->ip; mutex_unlock (&s->lock); /* Register the new PCB so that we can begin receiving * segments for it. */ tcp_list_add (&s->ip->tcp_sockets, ns); /* Parse any options in the SYN. */ tcp_parseopt (ns, h); /* Build an MSS option. */ optdata = HTONL (((unsigned long)2 << 24) | ((unsigned long)4 << 16) | (((unsigned long)ns->mss / 256) << 8) | (ns->mss & 255)); buf_free (p); /* Send a SYN|ACK together with the MSS option. * there is impossible fail of tcp_enqueue_option4 * */ tcp_enqueue_option4 (ns, TCP_SYN | TCP_ACK, optdata); #if TCP_LOCK_STYLE <= TCP_LOCK_SURE tcp_output (ns); mutex_unlock (&ns->ip->lock); #elif TCP_LOCK_STYLE >= TCP_LOCK_RELAXED mutex_unlock (&ns->ip->lock); tcp_output (ns); #endif mutex_unlock (&ns->lock); return ns; }
tcp_socket_t * tcp_accept (tcp_socket_t *s) { tcp_socket_t *ns; buf_t *p; ip_hdr_t *iph; tcp_hdr_t *h; unsigned long optdata; again: mutex_lock (&s->lock); for (;;) { if (s->state != LISTEN) { mutex_unlock (&s->lock); tcp_debug ("tcp_accept: called in invalid state\n"); return 0; } if (! tcp_queue_is_empty (s)) { p = tcp_queue_get (s); break; } mutex_wait (&s->lock); } mutex_unlock (&s->lock); /* Create a new PCB, and respond with a SYN|ACK. * If a new PCB could not be created (probably due to lack of memory), * we don't do anything, but rely on the sender will retransmit * the SYN at a time when we have more memory available. */ mutex_lock (&s->ip->lock); ns = tcp_alloc (s->ip); if (ns == 0) { tcp_debug ("tcp_accept: could not allocate PCB\n"); ++s->ip->tcp_in_discards; mutex_unlock (&s->ip->lock); buf_free (p); goto again; } h = (tcp_hdr_t*) p->payload; iph = ((ip_hdr_t*) p->payload) - 1; /* Set up the new PCB. */ memcpy (ns->local_ip, iph->dest, 4); ns->local_port = s->local_port; memcpy (ns->remote_ip, iph->src, 4); ns->remote_port = h->src; ns->state = SYN_RCVD; ns->rcv_nxt = s->ip->tcp_input_seqno + 1; ns->snd_wnd = h->wnd; ns->ssthresh = ns->snd_wnd; ns->snd_wl1 = s->ip->tcp_input_seqno; ns->ip = s->ip; /* Register the new PCB so that we can begin receiving * segments for it. */ tcp_list_add (&s->ip->tcp_sockets, ns); /* Parse any options in the SYN. */ tcp_parseopt (ns, h); /* Build an MSS option. */ optdata = HTONL (((unsigned long)2 << 24) | ((unsigned long)4 << 16) | (((unsigned long)ns->mss / 256) << 8) | (ns->mss & 255)); buf_free (p); /* Send a SYN|ACK together with the MSS option. */ tcp_enqueue (ns, 0, 0, TCP_SYN | TCP_ACK, (unsigned char*) &optdata, 4); tcp_output (ns); mutex_unlock (&s->ip->lock); return ns; }