int tcp_bind(struct tcp_pcb * __tp, in_addr_t __addr, uint16_t __port) { if (__tp == NULL) { DCC_LOG(LOG_WARNING, "NULL pointer"); /* FIXME: not a socket? The semantic here is not exactly the same as the sockets API. */ return -ENOTSOCK; } if (pcb_find((struct pcb *)__tp, &__tcp__.closed) < 0) { DCC_LOG1(LOG_ERROR, "<%04x> pcb_find()", (int)__tp); return -ENOTSOCK; } tcpip_net_lock(); if (__port == 0) { do { /* generate an ephemeral port number from 1024 to 33791 */ __port = ntohs(((__tcp__.port_seq++) & 0x7fff) + 1024); } while (!can_bind(__addr, __port)); } else { if (!can_bind(__addr, __port)) { DCC_LOG3(LOG_WARNING, "<%04x> %I:%d in use", (int)__tp, __addr, ntohs(__port)); return -EADDRINUSE; } } DCC_LOG3(LOG_TRACE, "<%05x> %I:%d", (int)__tp, __addr, ntohs(__port)); __tp->t_lport = __port; __tp->t_laddr = __addr; tcpip_net_unlock(); return 0; }
int tcp_close(struct tcp_pcb * __tp) { int ret; if (__tp == NULL) { DCC_LOG(LOG_WARNING, "NULL pointer"); return -1; } tcpip_net_lock(); #ifdef ENABLE_SANITY if ((pcb_find((struct pcb *)__tp, &__tcp__.active) < 0) && (pcb_find((struct pcb *)__tp, &__tcp__.listen) < 0) && pcb_find((struct pcb *)__tp, &__tcp__.closed) < 0) { DBG(DBG_ERROR, "<%05x> pcb_find()", (int)__tp); tcpip_net_unlock(); return -1; } #endif switch(__tp->t_state) { case TCPS_LISTEN: { ret = tcp_pcb_free(__tp); tcpip_net_unlock(); return ret; } case TCPS_TIME_WAIT: case TCPS_CLOSED: case TCPS_SYN_SENT: DCC_LOG2(LOG_TRACE, "<%05x> [%s]", (int)__tp, __tcp_state[__tp->t_state]); if (__tp->t_cond >= 0) { __os_cond_free(__tp->t_cond); __tp->t_cond = -1; } ret = tcp_pcb_free(__tp); tcpip_net_unlock(); return ret; /* active close */ case TCPS_SYN_RCVD: case TCPS_ESTABLISHED: /* Close the receive window */ /* * XXX: if we close the receive window we may stuck at * FIN_WAIT_2 state... */ // __tp->rcv_wnd = 0; __tp->t_state = TCPS_FIN_WAIT_1; DCC_LOG1(LOG_TRACE, "<%05x> [FIN_WAIT_1]", (int)__tp); break; /* passive close */ case TCPS_CLOSE_WAIT: __tp->t_state = TCPS_LAST_ACK; DCC_LOG1(LOG_TRACE, "<%05x> [LAST_ACK]", (int)__tp); /* discard the data * TODO: check whether both buffers must be * released or not. Probably they where released already. */ /* discards unsent data */ __tp->snd_off -= __tp->snd_q.len; __tp->snd_max -= __tp->snd_q.len; mbuf_queue_free(&__tp->snd_q); mbuf_queue_free(&__tp->rcv_q); /* notify the upper layer that we are closed */ break; default: { DCC_LOG2(LOG_ERROR, "<%05x> state=[%s]", (int)__tp, __tcp_state[__tp->t_state]); tcpip_net_unlock(); return -1; } } /* ACK now */ __tp->t_flags |= TF_ACKNOW; /* schedule output */ tcp_output_sched(__tp); tcpip_net_unlock(); return 0; }
int tcp_recv(struct tcp_pcb * __tp, void * __buf, int __len) { int n; if (__tp == NULL) { DCC_LOG(LOG_WARNING, "NULL pointer"); return -1; } if (__len == 0) { /* invalid argument */ DCC_LOG(LOG_WARNING, "invalid argument"); return -1; } tcpip_net_lock(); #ifdef ENABLE_SANITY_CHECK if (pcb_find((struct pcb *)__tp, &__tcp__.active) < 0) { DCC_LOG1(LOG_ERROR, "<%05x> pcb_find()", (int)__tp); tcpip_net_unlock(); return -1; } #endif for (;;) { if ((__tp->t_state == TCPS_CLOSED)) { DCC_LOG(LOG_WARNING, "closed!"); tcpip_net_unlock(); return -1; } if ((__tp->t_state == TCPS_TIME_WAIT) || (__tp->t_state == TCPS_CLOSING) || (__tp->t_state == TCPS_LAST_ACK)) { tcpip_net_unlock(); return 0; } if (__tp->rcv_q.len) break; if (__tp->t_state == TCPS_CLOSE_WAIT) { tcpip_net_unlock(); return 0; } DCC_LOG2(LOG_MSG, "<%05x> wait [%d]", (int)__tp, __tp->t_cond); thinkos_cond_wait(__tp->t_cond, net_mutex); } n = mbuf_queue_remove(&__tp->rcv_q, __buf, __len); DCC_LOG1(LOG_INFO, "len=%d", n); /* Half close: don't reopen the receiver window, i'm not sure whether it is a rule break or not, but it may prevent resources been consumed by an about to die connection! */ if ((__tp->t_state == TCPS_FIN_WAIT_1) || (__tp->t_state == TCPS_FIN_WAIT_2)) { DCC_LOG1(LOG_TRACE, "<%05x> FIN_WAIT", (int)__tp); tcpip_net_unlock(); return n; } /* XXX: revisit this ... */ // if ((__tp->rcv_q.len == 0) || (__tp->t_flags & TF_DELACK)) { if ((__tp->rcv_q.len == 0)) { if (__tp->t_flags & TF_DELACK) { __tp->t_flags |= TF_ACKNOW; } DCC_LOG(LOG_INFO, "empty queue, call tcp_out."); tcp_output_sched(__tp); } tcpip_net_unlock(); return n; }
int tcp_send(struct tcp_pcb * __tp, const void * __buf, int __len, int __flags) { uint8_t * src; int rem; int n; int m; if (__tp == NULL) { DCC_LOG(LOG_WARNING, "NULL pointer"); return -1; } #ifdef ENABLE_SANITY if (__buf == NULL) { DCC_LOG1(LOG_WARNING, "<%04x> NULL pointer:", (int)__tp); return -1; } if (__len < 0) { DCC_LOG2(LOG_WARNING, "<%04x> invalid length: %d", (int)__tp, __len); return -1; } #endif tcpip_net_lock(); #ifdef ENABLE_SANITY if (pcb_find((struct pcb *)__tp, &__tcp__.active) < 0) { DCC_LOG(LOG_ERROR, "<%04x> pcb_find()", (int)__tp); tcpip_net_unlock(); return -1; } #endif DCC_LOG3(LOG_INFO, "<%05x> buf=%05x len=%d", (int)__tp, (int)__buf, __len); src = (uint8_t *)__buf; rem = __len; again: if (__tp->t_state != TCPS_ESTABLISHED) { /* if ((__tp->t_state != TCPS_ESTABLISHED) && (__tp->t_state != TCPS_CLOSE_WAIT)) { */ DCC_LOG2(LOG_WARNING, "<%05x> [%s]", (int)__tp, __tcp_state[__tp->t_state]); if (__tp->t_state == TCPS_SYN_RCVD) { DCC_LOG1(LOG_TRACE, "<%05x> wait", (int)__tp); __os_cond_wait(__tp->t_cond, net_mutex); DCC_LOG2(LOG_TRACE, "<%05x> again [%s]", (int)__tp, __tcp_state[__tp->t_state]); goto again; } DCC_LOG(LOG_TRACE, "done."); tcpip_net_unlock(); return -1; } while (rem) { /* buffer limit ... */ m = tcp_maxsnd - __tp->snd_q.len; if (m <= 0) { DCC_LOG1(LOG_INFO, "<%05x> queue limit", (int)__tp); __tp->t_flags |= TF_ACKNOW; DCC_LOG(LOG_INFO, "output request."); tcp_output_sched(__tp); DCC_LOG(LOG_INFO, "waiting for buffer space."); __os_cond_wait(__tp->t_cond, net_mutex); goto again; } m = MIN(m, rem); if ((n = mbuf_queue_add(&__tp->snd_q, src, m)) == 0) { DCC_LOG(LOG_TRACE, "mbuf_wait..."); mbuf_wait(net_mutex); goto again; } rem -= n; src += n; } #if 0 /* FIXME: Set retransmit timer if not currently set, and not doing an ack or a keepalive probe. Initial value for retransmit is smoothed round-trip time + 2 * round-trip time variance. Initialize counter which is used for backoff :of retransmit time. */ if ((__tp->t_rxmt_tmr == 0) && (__tp->snd_una != 0)) { __tp->t_rxmt_tmr = tcp_rxmtintvl[0]; __tp->t_rxmt_cnt = 0; /* tp->t_flags &= ~TF_IDLE; */ } #endif if (__len > 0) { /* TCP_SEND_NOWAIT flag set or one maximum segment size pending for send then send now */ if ((__flags & TCP_SEND_NOWAIT) || ((__tp->snd_q.len - (int)__tp->snd_q.offs) >= __tp->t_maxseg)) { DCC_LOG(LOG_INFO, "output request."); tcp_output_sched(__tp); // if (tcp_output(__tp) < 0) { /* if the reason to fail was an arp failure try query an address pending for resolution ... */ // arp_query_pending(); // } } else { __tp->t_flags |= TF_DELACK; } } DCC_LOG(LOG_INFO, "done."); tcpip_net_unlock(); return __len; }
int udp_sendto(struct udp_pcb * __up, void * __buf, int __len, const struct sockaddr_in * __sin) { struct iphdr * ip; struct udphdr * uh; in_addr_t daddr; int dport; in_addr_t saddr; struct route * rt; #if (ENABLE_NET_UDP_CHECKSUM) unsigned int sum; #endif uint8_t * ptr; int mtu; struct ifnet * ifn; int ret; int retry = 0; DCC_LOG2(LOG_INFO, "<%05x> len=%d", (int)__up, __len); if (__up == NULL) { DCC_LOG1(LOG_WARNING, "<%05x> invalid pcb", (int)__up); return -EFAULT; } if (__buf == NULL) { DCC_LOG1(LOG_WARNING, "<%05x> invalid buffer", (int)__up); return -EFAULT; } tcpip_net_lock(); #if (ENABLE_NET_SANITY_CHECK) if (pcb_find((struct pcb *)__up, &__udp__.pcb) < 0) { DCC_LOG1(LOG_ERROR, "<%05x> pcb_find()", (int)__up); tcpip_net_unlock(); /* TODO: errno */ return -1; } #endif if ((__up->u_lport) == 0) { DCC_LOG1(LOG_WARNING, "<%05x> not bound", (int)__up); tcpip_net_unlock(); /* TODO: errno */ return -3; } if (__sin == NULL) { if ((dport = __up->u_fport) == 0) { DCC_LOG1(LOG_WARNING, "<%05x> connection refused", (int)__up); tcpip_net_unlock(); return -ECONNREFUSED; } if ((daddr = __up->u_faddr) == INADDR_ANY) { DCC_LOG1(LOG_WARNING, "<%05x> not connected", (int)__up); tcpip_net_unlock(); return -ENOTCONN; } } else { if ((dport = __sin->sin_port) == 0) { DCC_LOG1(LOG_WARNING, "<%05x> invalid port", (int)__up); tcpip_net_unlock(); return -ECONNREFUSED; } if ((daddr = __sin->sin_addr.s_addr) == INADDR_ANY) { DCC_LOG1(LOG_WARNING, "<%05x> invalid address", (int)__up); tcpip_net_unlock(); return -EDESTADDRREQ; } } if ((rt = __route_lookup(daddr)) == NULL) { DCC_LOG2(LOG_WARNING, "<%05x> no route to host: %I", (int)__up, daddr); tcpip_net_unlock(); UDP_PROTO_STAT_ADD(tx_drop, 1); UDP_PROTO_STAT_ADD(tx_err, 1); return -EHOSTUNREACH; } ifn = (struct ifnet *)rt->rt_ifn; mtu = ifn->if_mtu - sizeof(struct iphdr); if ((__len <= 0) || (__len > mtu)) { DCC_LOG3(LOG_WARNING, "<%04x> invalid length %d (max: %d)", (int)__up, __len, mtu); tcpip_net_unlock(); /* TODO: errno */ return -7; } /* get the source address */ if ((saddr = __up->u_laddr) == INADDR_ANY) { saddr = ifn->if_ipv4_addr; } again: ip = (struct iphdr *)ifn_mmap(ifn, sizeof(struct iphdr) + sizeof(struct udphdr) + __len); if (ip == NULL) { DCC_LOG1(LOG_WARNING, "<%04x> ifn_mmap() fail", (int)__up); tcpip_net_unlock(); /* TODO: errno */ return -1; } DCC_LOG2(LOG_TRACE, "<%05x> ip=%p", (int)__up, ip); iph_template(ip, IPPROTO_UDP, udp_def_ttl, udp_def_tos); uh = (struct udphdr *)ip->opt; /* build the ip header */ ip = mk_iphdr(ip, saddr, daddr, sizeof(struct udphdr) + __len); /* fill the udp header fields */ uh = (struct udphdr *)ip->opt; uh->dport = dport; uh->sport = __up->u_lport; uh->len = htons(__len + sizeof(struct udphdr)); #if (ENABLE_NET_UDP_CHECKSUM) /* initialize the udp checksum */ sum = uh->len << 1; sum += uh->dport; sum += uh->sport; sum += (IPPROTO_UDP << 8); sum += ((uint16_t *)(void *)&(ip->saddr))[0] + ((uint16_t *)(void *)&(ip->saddr))[1]; sum += ((uint16_t *)(void *)&(ip->daddr))[0] + ((uint16_t *)(void *)&(ip->daddr))[1]; #endif ptr = (uint8_t *)uh + sizeof(struct udphdr); memcpy(ptr, __buf, __len); #if (ENABLE_NET_UDP_CHECKSUM) if (__len) { sum = in_chksum(sum, ptr, __len); } uh->chksum = ~sum; #else uh->chksum = 0; #endif #if 0 DCC_LOG(LOG_INFO, "IP %d.%d.%d.%d:%d > %d.%d.%d.%d:%d: %d", IP4_ADDR1(ip->saddr), IP4_ADDR2(ip->saddr), IP4_ADDR3(ip->saddr), IP4_ADDR4(ip->saddr), ntohs(uh->sport), IP4_ADDR1(ip->daddr), IP4_ADDR2(ip->daddr), IP4_ADDR3(ip->daddr), IP4_ADDR4(ip->daddr), ntohs(uh->dport), ntohs(uh->len)); #endif if ((ret = ip_output(ifn, rt, ip)) < 0) { ifn_munmap(ifn, ip); /* if the reason to fail was an arp failure try query an address pending for resolution ... */ if ((ret == -EAGAIN) && (retry < 10)) { etharp_query_pending(); tcpip_net_unlock(); DCC_LOG2(LOG_WARNING, "<%05x> again, retry=%d!", (int)__up, retry); thinkos_sleep(10 + retry * 10); retry++; tcpip_net_lock(); goto again; } DCC_LOG1(LOG_ERROR, "<%05x> ip_output() fail!", (int)__up); UDP_PROTO_STAT_ADD(tx_drop, 1); } else { UDP_PROTO_STAT_ADD(tx_ok, 1); DCC_LOG5(LOG_INFO, "IP %I:%d > %I:%d: %d", ip->saddr, ntohs(uh->sport), ip->daddr, ntohs(uh->dport), ntohs(uh->len)); #if (LOG_LEVEL < LOG_INFO) DCC_LOG(LOG_INFO, "sent."); #endif ret = __len; } tcpip_net_unlock(); return ret; }