/* tcp.c Copyright 1995 Philip Homburg */ #include "inet.h" #include "buf.h" #include "clock.h" #include "event.h" #include "type.h" #if !CRAMPED #include "io.h" #include "ip.h" #endif #include "sr.h" #include "assert.h" #include "tcp.h" #include "tcp_int.h" THIS_FILE PUBLIC tcp_port_t *tcp_port_table; PUBLIC tcp_fd_t tcp_fd_table[TCP_FD_NR]; PUBLIC tcp_conn_t tcp_conn_table[TCP_CONN_NR]; FORWARD void tcp_main ARGS(( tcp_port_t *port )); FORWARD acc_t *tcp_get_data ARGS(( int fd, size_t offset, size_t count, int for_ioctl )); FORWARD int tcp_put_data ARGS(( int fd, size_t offset, acc_t *data, int for_ioctl )); FORWARD void tcp_put_pkt ARGS(( int fd, acc_t *data, size_t datalen )); FORWARD void read_ip_packets ARGS(( tcp_port_t *port )); FORWARD int tcp_setconf ARGS(( tcp_fd_t *tcp_fd )); FORWARD int tcp_setopt ARGS(( tcp_fd_t *tcp_fd )); FORWARD int tcp_connect ARGS(( tcp_fd_t *tcp_fd )); FORWARD int tcp_listen ARGS(( tcp_fd_t *tcp_fd )); FORWARD tcpport_t find_unused_port ARGS(( int fd )); FORWARD int is_unused_port ARGS(( Tcpport_t port )); FORWARD int reply_thr_put ARGS(( tcp_fd_t *tcp_fd, int reply, int for_ioctl )); FORWARD void reply_thr_get ARGS(( tcp_fd_t *tcp_fd, int reply, int for_ioctl )); FORWARD tcp_conn_t *find_conn_entry ARGS(( Tcpport_t locport, ipaddr_t locaddr, Tcpport_t remport, ipaddr_t readaddr )); FORWARD tcp_conn_t *find_empty_conn ARGS(( void )); FORWARD tcp_conn_t *find_best_conn ARGS(( ip_hdr_t *ip_hdr, tcp_hdr_t *tcp_hdr )); FORWARD int maybe_listen ARGS(( ipaddr_t locaddr, Tcpport_t locport, ipaddr_t remaddr, Tcpport_t remport )); FORWARD int conn_right4fd ARGS(( tcp_conn_t *tcp_conn, tcp_fd_t *tcp_fd )); FORWARD int tcp_su4connect ARGS(( tcp_fd_t *tcp_fd )); FORWARD void tcp_buffree ARGS(( int priority )); #ifdef BUF_CONSISTENCY_CHECK FORWARD void tcp_bufcheck ARGS(( void )); #endif FORWARD void tcp_setup_conn ARGS(( tcp_conn_t *tcp_conn )); PUBLIC void tcp_prep() { tcp_port_table= alloc(ip_conf_nr * sizeof(tcp_port_table[0])); } PUBLIC void tcp_init() { int i, j, k; tcp_fd_t *tcp_fd; tcp_port_t *tcp_port; tcp_conn_t *tcp_conn; assert (BUF_S >= sizeof(struct nwio_ipopt)); assert (BUF_S >= sizeof(struct nwio_ipconf)); assert (BUF_S >= sizeof(struct nwio_tcpconf)); assert (BUF_S >= IP_MAX_HDR_SIZE + TCP_MAX_HDR_SIZE); #if ZERO for (i=0, tcp_fd= tcp_fd_table; itf_flags= TFF_EMPTY; } for (i=0, tcp_conn= tcp_conn_table; itc_flags= TCF_EMPTY; tcp_conn->tc_busy= 0; } #endif #ifndef BUF_CONSISTENCY_CHECK bf_logon(tcp_buffree); #else bf_logon(tcp_buffree, tcp_bufcheck); #endif for (i=0, tcp_port= tcp_port_table; itp_ipdev= i; #if ZERO tcp_port->tp_flags= TPF_EMPTY; tcp_port->tp_state= TPS_EMPTY; tcp_port->tp_snd_head= NULL; tcp_port->tp_snd_tail= NULL; ev_init(&tcp_port->tp_snd_event); #endif for (j= 0; jtp_conn_hash[j][k]= &tcp_conn_table[0]; } } sr_add_minor(if2minor(ip_conf[i].ic_ifno, TCP_DEV_OFF), i, tcp_open, tcp_close, tcp_read, tcp_write, tcp_ioctl, tcp_cancel); tcp_main(tcp_port); } } PRIVATE void tcp_main(tcp_port) tcp_port_t *tcp_port; { int result, i; tcp_conn_t *tcp_conn; tcp_fd_t *tcp_fd; switch (tcp_port->tp_state) { case TPS_EMPTY: tcp_port->tp_state= TPS_SETPROTO; tcp_port->tp_ipfd= ip_open(tcp_port->tp_ipdev, tcp_port->tp_ipdev, tcp_get_data, tcp_put_data, tcp_put_pkt); if (tcp_port->tp_ipfd < 0) { tcp_port->tp_state= TPS_ERROR; DBLOCK(1, printf("%s, %d: unable to open ip port\n", __FILE__, __LINE__)); return; } result= ip_ioctl(tcp_port->tp_ipfd, NWIOSIPOPT); if (result == NW_SUSPEND) tcp_port->tp_flags |= TPF_SUSPEND; if (result < 0) { return; } if (tcp_port->tp_state != TPS_GETCONF) return; /* drops through */ case TPS_GETCONF: tcp_port->tp_flags &= ~TPF_SUSPEND; result= ip_ioctl(tcp_port->tp_ipfd, NWIOGIPCONF); if (result == NW_SUSPEND) tcp_port->tp_flags |= TPF_SUSPEND; if (result < 0) { return; } if (tcp_port->tp_state != TPS_MAIN) return; /* drops through */ case TPS_MAIN: tcp_port->tp_flags &= ~TPF_SUSPEND; tcp_port->tp_pack= 0; tcp_conn= &tcp_conn_table[tcp_port->tp_ipdev]; tcp_conn->tc_flags= TCF_INUSE; assert(!tcp_conn->tc_busy); tcp_conn->tc_locport= 0; tcp_conn->tc_locaddr= tcp_port->tp_ipaddr; tcp_conn->tc_remport= 0; tcp_conn->tc_remaddr= 0; tcp_conn->tc_state= TCS_CLOSED; tcp_conn->tc_fd= 0; tcp_conn->tc_connInprogress= 0; tcp_conn->tc_orglisten= FALSE; tcp_conn->tc_senddis= 0; tcp_conn->tc_ISS= 0; tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS; tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS; tcp_conn->tc_SND_UP= tcp_conn->tc_ISS; tcp_conn->tc_IRS= 0; tcp_conn->tc_RCV_LO= tcp_conn->tc_IRS; tcp_conn->tc_RCV_NXT= tcp_conn->tc_IRS; tcp_conn->tc_RCV_HI= tcp_conn->tc_IRS; tcp_conn->tc_RCV_UP= tcp_conn->tc_IRS; tcp_conn->tc_port= tcp_port; tcp_conn->tc_rcvd_data= NULL; tcp_conn->tc_adv_data= NULL; tcp_conn->tc_send_data= 0; tcp_conn->tc_remipopt= NULL; tcp_conn->tc_tcpopt= NULL; tcp_conn->tc_frag2send= 0; tcp_conn->tc_tos= TCP_DEF_TOS; tcp_conn->tc_ttl= IP_MAX_TTL; tcp_conn->tc_rcv_wnd= TCP_MAX_RCV_WND_SIZE; tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; tcp_conn->tc_stt= 0; tcp_conn->tc_0wnd_to= 0; tcp_conn->tc_rtt= TCP_DEF_RTT; tcp_conn->tc_mss= TCP_DEF_MSS; tcp_conn->tc_error= NW_OK; tcp_conn->tc_snd_wnd= TCP_MAX_SND_WND_SIZE; tcp_conn->tc_snd_cinc= (long)TCP_DEF_MSS*TCP_DEF_MSS/TCP_MAX_SND_WND_SIZE+1; tcp_conn->tc_rt_time= 0; tcp_conn->tc_rt_seq= 0; tcp_conn->tc_rt_threshold= tcp_conn->tc_ISS; for (i=0, tcp_fd= tcp_fd_table; itf_flags & TFF_INUSE)) continue; if (tcp_fd->tf_port != tcp_port) continue; if (tcp_fd->tf_flags & TFF_IOC_INIT_SP) { tcp_fd->tf_flags &= ~TFF_IOC_INIT_SP; tcp_ioctl(i, tcp_fd->tf_ioreq); } } read_ip_packets(tcp_port); return; #if !CRAMPED default: ip_panic(( "unknown state" )); #endif } } PRIVATE acc_t *tcp_get_data (port, offset, count, for_ioctl) int port; size_t offset; size_t count; int for_ioctl; { tcp_port_t *tcp_port; int result; tcp_port= &tcp_port_table[port]; switch (tcp_port->tp_state) { case TPS_SETPROTO: if (!count) { result= (int)offset; if (result<0) { tcp_port->tp_state= TPS_ERROR; break; } tcp_port->tp_state= TPS_GETCONF; if (tcp_port->tp_flags & TPF_SUSPEND) tcp_main(tcp_port); return NW_OK; } assert (!offset); assert (count == sizeof(struct nwio_ipopt)); { struct nwio_ipopt *ipopt; acc_t *acc; acc= bf_memreq(sizeof(*ipopt)); ipopt= (struct nwio_ipopt *)ptr2acc_data(acc); ipopt->nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_DI_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL; ipopt->nwio_proto= IPPROTO_TCP; return acc; } case TPS_MAIN: assert(tcp_port->tp_flags & TPF_WRITE_IP); if (!count) { result= (int)offset; if (result<0) { if (result == EDSTNOTRCH) { if (tcp_port->tp_snd_head) { tcp_notreach(tcp_port-> tp_snd_head); } } else { ip_warning(( "ip_write failed with error: %d\n", result )); } } assert (tcp_port->tp_pack); bf_afree (tcp_port->tp_pack); tcp_port->tp_pack= 0; if (tcp_port->tp_flags & TPF_WRITE_SP) { tcp_port->tp_flags &= ~(TPF_WRITE_SP| TPF_WRITE_IP); if (tcp_port->tp_snd_head) tcp_port_write(tcp_port); } else tcp_port->tp_flags &= ~TPF_WRITE_IP; } else { return bf_cut (tcp_port->tp_pack, offset, count); } break; default: #if !CRAMPED printf("tcp_get_data(%d, 0x%x, 0x%x) called but tp_state= 0x%x\n", port, offset, count, tcp_port->tp_state); #endif break; } return NW_OK; } PRIVATE int tcp_put_data (fd, offset, data, for_ioctl) int fd; size_t offset; acc_t *data; int for_ioctl; { tcp_port_t *tcp_port; int result; tcp_port= &tcp_port_table[fd]; switch (tcp_port->tp_state) { case TPS_GETCONF: if (!data) { result= (int)offset; if (result<0) { tcp_port->tp_state= TPS_ERROR; return NW_OK; } tcp_port->tp_state= TPS_MAIN; if (tcp_port->tp_flags & TPF_SUSPEND) tcp_main(tcp_port); } else { struct nwio_ipconf *ipconf; data= bf_packIffLess(data, sizeof(*ipconf)); ipconf= (struct nwio_ipconf *)ptr2acc_data(data); assert (ipconf->nwic_flags & NWIC_IPADDR_SET); tcp_port->tp_ipaddr= ipconf->nwic_ipaddr; bf_afree(data); } break; case TPS_MAIN: assert(tcp_port->tp_flags & TPF_READ_IP); if (!data) { result= (int)offset; if (result<0) ip_panic(( "ip_read() failed" )); if (tcp_port->tp_flags & TPF_READ_SP) { tcp_port->tp_flags &= ~(TPF_READ_SP| TPF_READ_IP); read_ip_packets(tcp_port); } else tcp_port->tp_flags &= ~TPF_READ_IP; } else { assert(!offset); /* this is an invalid assertion but ip sends * only whole datagrams up */ tcp_put_pkt(fd, data, bf_bufsize(data)); } break; default: #if !CRAMPED printf("tcp_put_data(%d, 0x%x, 0x%x) called but tp_state= 0x%x\n", fd, offset, data, tcp_port->tp_state); #endif break; } return NW_OK; } /* tcp_put_pkt */ PRIVATE void tcp_put_pkt(fd, data, datalen) int fd; acc_t *data; size_t datalen; { tcp_port_t *tcp_port; tcp_conn_t *tcp_conn, **conn_p; ip_hdr_t *ip_hdr; tcp_hdr_t *tcp_hdr; acc_t *ip_pack, *tcp_pack; size_t ip_datalen, tcp_datalen, ip_hdr_len, tcp_hdr_len; u16_t sum; u32_t bits; int hash; ipaddr_t srcaddr, dstaddr; tcpport_t srcport, dstport; tcp_port= &tcp_port_table[fd]; /* Extract the IP header. */ ip_hdr= (ip_hdr_t *)ptr2acc_data(data); ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; ip_datalen= datalen - ip_hdr_len; if (ip_datalen == 0) { DBLOCK(1, printf("tcp_put_pkt: no TCP header\n")); bf_afree(data); return; } data->acc_linkC++; ip_pack= data; ip_pack= bf_align(ip_pack, ip_hdr_len, 4); ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack); data= bf_delhead(data, ip_hdr_len); /* Compute the checksum */ sum= tcp_pack_oneCsum(ip_hdr, data); /* Extract the TCP header */ if (ip_datalen < TCP_MIN_HDR_SIZE) { DBLOCK(1, printf("truncated TCP header\n")); bf_afree(ip_pack); bf_afree(data); return; } data= bf_packIffLess(data, TCP_MIN_HDR_SIZE); tcp_hdr= (tcp_hdr_t *)ptr2acc_data(data); tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2; /* actualy (>> 4) << 2 */ if (ip_datalen < tcp_hdr_len || tcp_hdr_len < TCP_MIN_HDR_SIZE) { if (tcp_hdr_len < TCP_MIN_HDR_SIZE) { DBLOCK(1, printf("strange tcp header length %d\n", tcp_hdr_len)); } else { DBLOCK(1, printf("truncated TCP header\n")); } bf_afree(ip_pack); bf_afree(data); return; } data->acc_linkC++; tcp_pack= data; tcp_pack= bf_align(tcp_pack, tcp_hdr_len, 4); tcp_hdr= (tcp_hdr_t *)ptr2acc_data(tcp_pack); if (ip_datalen == tcp_hdr_len) { bf_afree(data); data= NULL; } else data= bf_delhead(data, tcp_hdr_len); tcp_datalen= ip_datalen-tcp_hdr_len; if ((u16_t)~sum) { DBLOCK(1, printf("checksum error in tcp packet\n"); printf("tcp_pack_oneCsum(...)= 0x%x length= %d\n", (u16_t)~sum, tcp_datalen); printf("src ip_addr= "); writeIpAddr(ip_hdr->ih_src); printf("\n")); bf_afree(ip_pack); bf_afree(tcp_pack); bf_afree(data); return; } srcaddr= ip_hdr->ih_src; dstaddr= ip_hdr->ih_dst; srcport= tcp_hdr->th_srcport; dstport= tcp_hdr->th_dstport; bits= srcaddr ^ dstaddr ^ srcport ^ dstport; bits= (bits >> 16) ^ bits; bits= (bits >> 8) ^ bits; hash= ((bits >> TCP_CONN_HASH_SHIFT) ^ bits) & (TCP_CONN_HASH_NR-1); conn_p= tcp_port->tp_conn_hash[hash]; if (conn_p[0]->tc_locport == dstport && conn_p[0]->tc_remport == srcport && conn_p[0]->tc_remaddr == srcaddr && conn_p[0]->tc_locaddr == dstaddr) { tcp_conn= conn_p[0]; } else if (conn_p[1]->tc_locport == dstport && conn_p[1]->tc_remport == srcport && conn_p[1]->tc_remaddr == srcaddr && conn_p[1]->tc_locaddr == dstaddr) { tcp_conn= conn_p[1]; conn_p[1]= conn_p[0]; conn_p[0]= tcp_conn; } else if (conn_p[2]->tc_locport == dstport && conn_p[2]->tc_remport == srcport && conn_p[2]->tc_remaddr == srcaddr && conn_p[2]->tc_locaddr == dstaddr) { tcp_conn= conn_p[2]; conn_p[2]= conn_p[1]; conn_p[1]= conn_p[0]; conn_p[0]= tcp_conn; } else if (conn_p[3]->tc_locport == dstport && conn_p[3]->tc_remport == srcport && conn_p[3]->tc_remaddr == srcaddr && conn_p[3]->tc_locaddr == dstaddr) { tcp_conn= conn_p[3]; conn_p[3]= conn_p[2]; conn_p[2]= conn_p[1]; conn_p[1]= conn_p[0]; conn_p[0]= tcp_conn; } else tcp_conn= NULL; if (tcp_conn != NULL && tcp_conn->tc_state == TCS_CLOSED || (tcp_hdr->th_flags & THF_SYN)) { tcp_conn= NULL; } if (tcp_conn == NULL) { tcp_conn= find_best_conn(ip_hdr, tcp_hdr); if (!tcp_conn) { /* listen backlog hack */ bf_afree(ip_pack); bf_afree(tcp_pack); bf_afree(data); return; } if (tcp_conn->tc_state != TCS_CLOSED) { conn_p[3]= conn_p[2]; conn_p[2]= conn_p[1]; conn_p[1]= conn_p[0]; conn_p[0]= tcp_conn; } } assert(tcp_conn->tc_busy == 0); tcp_conn->tc_busy++; tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, data, tcp_datalen); tcp_conn->tc_busy--; bf_afree(ip_pack); bf_afree(tcp_pack); } PUBLIC int tcp_open (port, srfd, get_userdata, put_userdata, put_pkt) int port; int srfd; get_userdata_t get_userdata; put_userdata_t put_userdata; put_pkt_t put_pkt; { int i; tcp_fd_t *tcp_fd; for (i=0; i=TCP_FD_NR) { return EAGAIN; } tcp_fd= &tcp_fd_table[i]; tcp_fd->tf_flags= TFF_INUSE; tcp_fd->tf_flags |= TFF_PUSH_DATA; /* XXX */ tcp_fd->tf_port= &tcp_port_table[port]; tcp_fd->tf_srfd= srfd; tcp_fd->tf_tcpconf.nwtc_flags= TCP_DEF_CONF; tcp_fd->tf_tcpconf.nwtc_remaddr= 0; tcp_fd->tf_tcpconf.nwtc_remport= 0; tcp_fd->tf_tcpopt.nwto_flags= TCP_DEF_OPT; tcp_fd->tf_get_userdata= get_userdata; tcp_fd->tf_put_userdata= put_userdata; tcp_fd->tf_conn= 0; return i; } /* tcp_ioctl */ PUBLIC int tcp_ioctl (fd, req) int fd; ioreq_t req; { tcp_fd_t *tcp_fd; tcp_port_t *tcp_port; tcp_conn_t *tcp_conn; nwio_tcpconf_t *tcp_conf; nwio_tcpopt_t *tcp_opt; acc_t *conf_acc, *opt_acc; int result; tcp_fd= &tcp_fd_table[fd]; assert (tcp_fd->tf_flags & TFF_INUSE); tcp_port= tcp_fd->tf_port; tcp_fd->tf_flags |= TFF_IOCTL_IP; tcp_fd->tf_ioreq= req; if (tcp_port->tp_state != TPS_MAIN) { tcp_fd->tf_flags |= TFF_IOC_INIT_SP; return NW_SUSPEND; } switch (req) { case NWIOSTCPCONF: if (tcp_fd->tf_flags & TFF_CONNECTED) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, EISCONN, TRUE); result= NW_OK; break; } result= tcp_setconf(tcp_fd); break; case NWIOGTCPCONF: conf_acc= bf_memreq(sizeof(*tcp_conf)); assert (conf_acc->acc_length == sizeof(*tcp_conf)); tcp_conf= (nwio_tcpconf_t *)ptr2acc_data(conf_acc); *tcp_conf= tcp_fd->tf_tcpconf; if (tcp_fd->tf_flags & TFF_CONNECTED) { tcp_conn= tcp_fd->tf_conn; tcp_conf->nwtc_locport= tcp_conn->tc_locport; tcp_conf->nwtc_remaddr= tcp_conn->tc_remaddr; tcp_conf->nwtc_remport= tcp_conn->tc_remport; } tcp_conf->nwtc_locaddr= tcp_fd->tf_port->tp_ipaddr; result= (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, 0, conf_acc, TRUE); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_put(tcp_fd, result, TRUE); result= NW_OK; break; case NWIOSTCPOPT: result= tcp_setopt(tcp_fd); break; case NWIOGTCPOPT: opt_acc= bf_memreq(sizeof(*tcp_opt)); assert (opt_acc->acc_length == sizeof(*tcp_opt)); tcp_opt= (nwio_tcpopt_t *)ptr2acc_data(opt_acc); *tcp_opt= tcp_fd->tf_tcpopt; result= (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, 0, opt_acc, TRUE); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_put(tcp_fd, result, TRUE); result= NW_OK; break; case NWIOTCPCONN: if (tcp_fd->tf_flags & TFF_CONNECTED) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, EISCONN, TRUE); result= NW_OK; break; } result= tcp_connect(tcp_fd); break; case NWIOTCPLISTEN: if (tcp_fd->tf_flags & TFF_CONNECTED) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, EISCONN, TRUE); result= NW_OK; break; } result= tcp_listen(tcp_fd); break; case NWIOTCPSHUTDOWN: if (!(tcp_fd->tf_flags & TFF_CONNECTED)) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, ENOTCONN, TRUE); result= NW_OK; break; } tcp_fd->tf_flags |= TFF_IOCTL_IP; tcp_fd->tf_ioreq= req; tcp_conn= tcp_fd->tf_conn; tcp_conn->tc_busy++; tcp_fd_write(tcp_conn); tcp_conn->tc_busy--; tcp_conn_write(tcp_conn, 0); if (!(tcp_fd->tf_flags & TFF_IOCTL_IP)) return NW_OK; else return NW_SUSPEND; break; default: tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADIOCTL, TRUE); result= NW_OK; break; } return result; } /* tcp_setconf */ PRIVATE int tcp_setconf(tcp_fd) tcp_fd_t *tcp_fd; { nwio_tcpconf_t *tcpconf; nwio_tcpconf_t oldconf, newconf; acc_t *data; int result; tcpport_t port; tcp_fd_t *fd_ptr; unsigned int new_en_flags, new_di_flags, old_en_flags, old_di_flags, all_flags, flags; int i; data= (*tcp_fd->tf_get_userdata) (tcp_fd->tf_srfd, 0, sizeof(nwio_tcpconf_t), TRUE); if (!data) return EFAULT; data= bf_packIffLess(data, sizeof(nwio_tcpconf_t)); assert (data->acc_length == sizeof(nwio_tcpconf_t)); tcpconf= (nwio_tcpconf_t *)ptr2acc_data(data); oldconf= tcp_fd->tf_tcpconf; newconf= *tcpconf; old_en_flags= oldconf.nwtc_flags & 0xffff; old_di_flags= (oldconf.nwtc_flags >> 16) & 0xffff; new_en_flags= newconf.nwtc_flags & 0xffff; new_di_flags= (newconf.nwtc_flags >> 16) & 0xffff; if (new_en_flags & new_di_flags) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; } /* NWTC_ACC_MASK */ if (new_di_flags & NWTC_ACC_MASK) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; /* access modes can't be disabled */ } if (!(new_en_flags & NWTC_ACC_MASK)) new_en_flags |= (old_en_flags & NWTC_ACC_MASK); /* NWTC_LOCPORT_MASK */ if (new_di_flags & NWTC_LOCPORT_MASK) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; /* the loc ports can't be disabled */ } if (!(new_en_flags & NWTC_LOCPORT_MASK)) { new_en_flags |= (old_en_flags & NWTC_LOCPORT_MASK); newconf.nwtc_locport= oldconf.nwtc_locport; } else if ((new_en_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SEL) { newconf.nwtc_locport= find_unused_port(tcp_fd- tcp_fd_table); } else if ((new_en_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SET) { if (!newconf.nwtc_locport) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; } } /* NWTC_REMADDR_MASK */ if (!((new_en_flags | new_di_flags) & NWTC_REMADDR_MASK)) { new_en_flags |= (old_en_flags & NWTC_REMADDR_MASK); new_di_flags |= (old_di_flags & NWTC_REMADDR_MASK); newconf.nwtc_remaddr= oldconf.nwtc_remaddr; } else if (new_en_flags & NWTC_SET_RA) { if (!newconf.nwtc_remaddr) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; } } else { assert (new_di_flags & NWTC_REMADDR_MASK); newconf.nwtc_remaddr= 0; } /* NWTC_REMPORT_MASK */ if (!((new_en_flags | new_di_flags) & NWTC_REMPORT_MASK)) { new_en_flags |= (old_en_flags & NWTC_REMPORT_MASK); new_di_flags |= (old_di_flags & NWTC_REMPORT_MASK); newconf.nwtc_remport= oldconf.nwtc_remport; } else if (new_en_flags & NWTC_SET_RP) { if (!newconf.nwtc_remport) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); bf_afree(data); return NW_OK; } } else { assert (new_di_flags & NWTC_REMPORT_MASK); newconf.nwtc_remport= 0; } newconf.nwtc_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; all_flags= new_en_flags | new_di_flags; /* check the access modes */ if ((all_flags & NWTC_LOCPORT_MASK) != NWTC_LP_UNSET) { for (i=0, fd_ptr= tcp_fd_table; itf_flags & TFF_INUSE)) continue; if (fd_ptr->tf_port != tcp_fd->tf_port) continue; flags= fd_ptr->tf_tcpconf.nwtc_flags; if ((flags & NWTC_LOCPORT_MASK) == NWTC_LP_UNSET) continue; if (fd_ptr->tf_tcpconf.nwtc_locport != newconf.nwtc_locport) continue; if ((flags & NWTC_ACC_MASK) != (all_flags & NWTC_ACC_MASK) || (all_flags & NWTC_ACC_MASK) == NWTC_EXCL) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EADDRINUSE, TRUE); bf_afree(data); return NW_OK; } } } tcp_fd->tf_tcpconf= newconf; if ((all_flags & NWTC_ACC_MASK) && ((all_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SET || (all_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SEL) && (all_flags & NWTC_REMADDR_MASK) && (all_flags & NWTC_REMPORT_MASK)) tcp_fd->tf_flags |= TFF_CONF_SET; else { tcp_fd->tf_flags &= ~TFF_CONF_SET; } bf_afree(data); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, NW_OK, TRUE); return NW_OK; } /* tcp_setopt */ PRIVATE int tcp_setopt(tcp_fd) tcp_fd_t *tcp_fd; { nwio_tcpopt_t *tcpopt; nwio_tcpopt_t oldopt, newopt; acc_t *data; int result; tcpport_t port; tcp_fd_t *fd_ptr; unsigned int new_en_flags, new_di_flags, old_en_flags, old_di_flags, all_flags, flags; int i; data= (*tcp_fd->tf_get_userdata) (tcp_fd->tf_srfd, 0, sizeof(nwio_tcpopt_t), TRUE); if (!data) return EFAULT; data= bf_packIffLess(data, sizeof(nwio_tcpopt_t)); assert (data->acc_length == sizeof(nwio_tcpopt_t)); tcpopt= (nwio_tcpopt_t *)ptr2acc_data(data); oldopt= tcp_fd->tf_tcpopt; newopt= *tcpopt; old_en_flags= oldopt.nwto_flags & 0xffff; old_di_flags= (oldopt.nwto_flags >> 16) & 0xffff; new_en_flags= newopt.nwto_flags & 0xffff; new_di_flags= (newopt.nwto_flags >> 16) & 0xffff; if (new_en_flags & new_di_flags) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); return NW_OK; } /* NWTO_SND_URG_MASK */ if (!((new_en_flags | new_di_flags) & NWTO_SND_URG_MASK)) { new_en_flags |= (old_en_flags & NWTO_SND_URG_MASK); new_di_flags |= (old_di_flags & NWTO_SND_URG_MASK); } /* NWTO_RCV_URG_MASK */ if (!((new_en_flags | new_di_flags) & NWTO_RCV_URG_MASK)) { new_en_flags |= (old_en_flags & NWTO_RCV_URG_MASK); new_di_flags |= (old_di_flags & NWTO_RCV_URG_MASK); } /* NWTO_BSD_URG_MASK */ if (!((new_en_flags | new_di_flags) & NWTO_BSD_URG_MASK)) { new_en_flags |= (old_en_flags & NWTO_BSD_URG_MASK); new_di_flags |= (old_di_flags & NWTO_BSD_URG_MASK); } else { if (tcp_fd->tf_conn == NULL) { bf_afree(data); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EINVAL, TRUE); return NW_OK; } } /* NWTO_DEL_RST_MASK */ if (!((new_en_flags | new_di_flags) & NWTO_DEL_RST_MASK)) { new_en_flags |= (old_en_flags & NWTO_DEL_RST_MASK); new_di_flags |= (old_di_flags & NWTO_DEL_RST_MASK); } newopt.nwto_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; tcp_fd->tf_tcpopt= newopt; if (newopt.nwto_flags & NWTO_SND_URG) tcp_fd->tf_flags |= TFF_WR_URG; else tcp_fd->tf_flags &= ~TFF_WR_URG; if (newopt.nwto_flags & NWTO_RCV_URG) tcp_fd->tf_flags |= TFF_RECV_URG; else tcp_fd->tf_flags &= ~TFF_RECV_URG; if (tcp_fd->tf_conn) { if (newopt.nwto_flags & NWTO_BSD_URG) { tcp_fd->tf_conn->tc_flags |= TCF_BSD_URG; } else { tcp_fd->tf_conn->tc_flags &= ~TCF_BSD_URG; } } if (newopt.nwto_flags & NWTO_DEL_RST) tcp_fd->tf_flags |= TFF_DEL_RST; else tcp_fd->tf_flags &= ~TFF_DEL_RST; bf_afree(data); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, NW_OK, TRUE); return NW_OK; } PRIVATE tcpport_t find_unused_port(fd) int fd; { tcpport_t port, nw_port; nw_port= htons(0xC000+fd); if (is_unused_port(nw_port)) return nw_port; for (port= 0xC000+TCP_FD_NR; port < 0xFFFF; port++) { nw_port= htons(port); if (is_unused_port(nw_port)) return nw_port; } #if !CRAMPED ip_panic(( "unable to find unused port (shouldn't occur)" )); return 0; #endif } PRIVATE int is_unused_port(port) tcpport_t port; { int i; tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; for (i= 0, tcp_fd= tcp_fd_table; itf_flags & TFF_CONF_SET)) continue; if (tcp_fd->tf_tcpconf.nwtc_locport == port) return FALSE; } for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; itc_flags & TCF_INUSE)) continue; if (tcp_conn->tc_locport == port) return FALSE; } return TRUE; } PRIVATE int reply_thr_put(tcp_fd, reply, for_ioctl) tcp_fd_t *tcp_fd; int reply; int for_ioctl; { assert (tcp_fd); return (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, reply, (acc_t *)0, for_ioctl); } PRIVATE void reply_thr_get(tcp_fd, reply, for_ioctl) tcp_fd_t *tcp_fd; int reply; int for_ioctl; { acc_t *result; result= (*tcp_fd->tf_get_userdata)(tcp_fd->tf_srfd, reply, (size_t)0, for_ioctl); assert (!result); } PUBLIC int tcp_su4listen(tcp_fd) tcp_fd_t *tcp_fd; { tcp_conn_t *tcp_conn; acc_t *tmp_acc; tcp_conn= tcp_fd->tf_conn; tcp_conn->tc_locport= tcp_fd->tf_tcpconf.nwtc_locport; tcp_conn->tc_locaddr= tcp_fd->tf_port->tp_ipaddr; if (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP) tcp_conn->tc_remport= tcp_fd->tf_tcpconf.nwtc_remport; else tcp_conn->tc_remport= 0; if (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA) tcp_conn->tc_remaddr= tcp_fd->tf_tcpconf.nwtc_remaddr; else tcp_conn->tc_remaddr= 0; tcp_setup_conn(tcp_conn); tcp_conn->tc_port= tcp_fd->tf_port; tcp_conn->tc_fd= tcp_fd; tcp_conn->tc_connInprogress= 1; tcp_conn->tc_orglisten= TRUE; tcp_conn->tc_state= TCS_LISTEN; tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_LISTEN; return NW_SUSPEND; } /* find_empty_conn This function returns a connection that is not inuse. This includes connections that are never used, and connections without a user that are not used for a while. */ PRIVATE tcp_conn_t *find_empty_conn() { int i; tcp_conn_t *tcp_conn; int state; for (i=ip_conf_nr, tcp_conn= tcp_conn_table+i; itc_flags == TCF_EMPTY) { tcp_conn->tc_connInprogress= 0; tcp_conn->tc_fd= NULL; return tcp_conn; } if (tcp_conn->tc_fd) continue; if (tcp_conn->tc_senddis > get_time()) continue; if (tcp_conn->tc_state != TCS_CLOSED) { tcp_close_connection (tcp_conn, ENOCONN); } tcp_conn->tc_flags= 0; return tcp_conn; } return NULL; } /* find_conn_entry This function return a connection matching locport, locaddr, remport, remaddr. If no such connection exists NULL is returned. If a connection exists without mainuser it is closed. */ PRIVATE tcp_conn_t *find_conn_entry(locport, locaddr, remport, remaddr) tcpport_t locport; ipaddr_t locaddr; tcpport_t remport; ipaddr_t remaddr; { tcp_conn_t *tcp_conn; int i, state; assert(remport); assert(remaddr); for (i=ip_conf_nr, tcp_conn= tcp_conn_table+i; itc_flags == TCF_EMPTY) continue; if (tcp_conn->tc_locport != locport || tcp_conn->tc_locaddr != locaddr || tcp_conn->tc_remport != remport || tcp_conn->tc_remaddr != remaddr) continue; if (tcp_conn->tc_fd) return tcp_conn; state= tcp_conn->tc_state; if (state != TCS_CLOSED) { tcp_close_connection(tcp_conn, ENOCONN); } return tcp_conn; } return NULL; } PRIVATE void read_ip_packets(tcp_port) tcp_port_t *tcp_port; { int result; do { tcp_port->tp_flags |= TPF_READ_IP; result= ip_read(tcp_port->tp_ipfd, TCP_MAX_DATAGRAM); if (result == NW_SUSPEND) { tcp_port->tp_flags |= TPF_READ_SP; return; } assert(result == NW_OK); tcp_port->tp_flags &= ~TPF_READ_IP; } while(!(tcp_port->tp_flags & TPF_READ_IP)); } /* find_best_conn */ PRIVATE tcp_conn_t *find_best_conn(ip_hdr, tcp_hdr) ip_hdr_t *ip_hdr; tcp_hdr_t *tcp_hdr; { int best_level, new_level; tcp_conn_t *best_conn, *listen_conn, *tcp_conn; tcp_fd_t *tcp_fd; int i; ipaddr_t locaddr; ipaddr_t remaddr; tcpport_t locport; tcpport_t remport; locaddr= ip_hdr->ih_dst; remaddr= ip_hdr->ih_src; locport= tcp_hdr->th_dstport; remport= tcp_hdr->th_srcport; if (!remport) /* This can interfere with a listen, so we reject it * by clearing the requested port */ locport= 0; best_level= 0; best_conn= NULL; listen_conn= NULL; for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; itc_flags & TCF_INUSE)) continue; /* First fast check for open connections. */ if (tcp_conn->tc_locaddr == locaddr && tcp_conn->tc_locport == locport && tcp_conn->tc_remport == remport && tcp_conn->tc_remaddr == remaddr && tcp_conn->tc_fd) { return tcp_conn; } /* Now check for listens and abandoned connections. */ if (tcp_conn->tc_locaddr != locaddr) { continue; } new_level= 0; if (tcp_conn->tc_locport) { if (tcp_conn->tc_locport != locport) { continue; } new_level += 4; } if (tcp_conn->tc_remport) { if (tcp_conn->tc_remport != remport) { continue; } new_level += 1; } if (tcp_conn->tc_remaddr) { if (tcp_conn->tc_remaddr != remaddr) { continue; } new_level += 2; } if (new_leveltc_state != TCS_LISTEN) continue; if (new_level == 7 && !tcp_conn->tc_fd) /* We found an abandoned connection */ { if (best_conn && tcp_Lmod4G(tcp_conn->tc_ISS, best_conn->tc_ISS)) { continue; } best_conn= tcp_conn; continue; } if (!(tcp_hdr->th_flags & THF_SYN)) continue; best_level= new_level; listen_conn= tcp_conn; } if (!best_conn && !listen_conn) { if ((tcp_hdr->th_flags & THF_SYN) && maybe_listen(locaddr, locport, remaddr, remport)) { /* Quick hack to implement listen back logs: * if a SYN arrives and there is no listen waiting * for that packet, then no reply is sent. */ return NULL; } for (i=0, tcp_conn= tcp_conn_table; itc_flags & TCF_INUSE) && tcp_conn->tc_locaddr==locaddr) { break; } } assert (tcp_conn); assert (tcp_conn->tc_state == TCS_CLOSED); tcp_conn->tc_locport= locport; tcp_conn->tc_locaddr= locaddr; tcp_conn->tc_remport= remport; tcp_conn->tc_remaddr= remaddr; assert (!tcp_conn->tc_fd); return tcp_conn; } if (best_conn) { assert(!best_conn->tc_fd); if (!listen_conn) return best_conn; tcp_fd= listen_conn->tc_fd; assert(tcp_fd && listen_conn->tc_connInprogress && tcp_fd->tf_conn == listen_conn); if (best_conn->tc_state != TCS_CLOSED) tcp_close_connection(best_conn, ENOCONN); listen_conn->tc_ISS= best_conn->tc_ISS; if (best_conn->tc_senddis > listen_conn->tc_senddis) listen_conn->tc_senddis= best_conn->tc_senddis; return listen_conn; } assert (listen_conn); return listen_conn; } /* maybe_listen */ PRIVATE int maybe_listen(locaddr, locport, remaddr, remport) ipaddr_t locaddr; tcpport_t locport; ipaddr_t remaddr; tcpport_t remport; { int i; tcp_conn_t *tcp_conn; tcp_fd_t *fd; for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; itc_flags & TCF_INUSE)) continue; if (tcp_conn->tc_locaddr != locaddr) { continue; } if (tcp_conn->tc_locport != locport ) { continue; } if (!tcp_conn->tc_orglisten) continue; fd= tcp_conn->tc_fd; if (!fd) continue; if ((fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP) && tcp_conn->tc_remport != remport) { continue; } if ((fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA) && tcp_conn->tc_remaddr != remaddr) { continue; } if (!(fd->tf_flags & TFF_DEL_RST)) continue; return 1; } return 0; } PUBLIC void tcp_reply_ioctl(tcp_fd, reply) tcp_fd_t *tcp_fd; int reply; { assert (tcp_fd->tf_flags & TFF_IOCTL_IP); assert (tcp_fd->tf_ioreq == NWIOTCPSHUTDOWN || tcp_fd->tf_ioreq == NWIOTCPLISTEN || tcp_fd->tf_ioreq == NWIOTCPCONN); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, reply, TRUE); } PUBLIC void tcp_reply_write(tcp_fd, reply) tcp_fd_t *tcp_fd; size_t reply; { assert (tcp_fd->tf_flags & TFF_WRITE_IP); tcp_fd->tf_flags &= ~TFF_WRITE_IP; reply_thr_get (tcp_fd, reply, FALSE); } PUBLIC void tcp_reply_read(tcp_fd, reply) tcp_fd_t *tcp_fd; size_t reply; { assert (tcp_fd->tf_flags & TFF_READ_IP); tcp_fd->tf_flags &= ~TFF_READ_IP; reply_thr_put (tcp_fd, reply, FALSE); } PUBLIC int tcp_write(fd, count) int fd; size_t count; { tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; tcp_fd= &tcp_fd_table[fd]; assert (tcp_fd->tf_flags & TFF_INUSE); if (!(tcp_fd->tf_flags & TFF_CONNECTED)) { reply_thr_get (tcp_fd, ENOTCONN, FALSE); return NW_OK; } tcp_conn= tcp_fd->tf_conn; if (tcp_conn->tc_state == TCS_CLOSED) { reply_thr_get(tcp_fd, tcp_conn->tc_error, FALSE); return NW_OK; } if (tcp_conn->tc_flags & TCF_FIN_SENT) { reply_thr_get (tcp_fd, ESHUTDOWN, FALSE); return NW_OK; } tcp_fd->tf_flags |= TFF_WRITE_IP; tcp_fd->tf_write_offset= 0; tcp_fd->tf_write_count= count; /* Start the timer (if necessary) */ if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT && tcp_conn->tc_transmit_seq == tcp_conn->tc_SND_UNA) { tcp_set_send_timer(tcp_conn); } assert(tcp_conn->tc_transmit_timer.tim_active || (tcp_print_conn(tcp_conn), printf("\n"), 0)); assert(tcp_conn->tc_busy == 0); tcp_conn->tc_busy++; tcp_fd_write(tcp_conn); tcp_conn->tc_busy--; tcp_conn_write(tcp_conn, 0); if (!(tcp_fd->tf_flags & TFF_WRITE_IP)) return NW_OK; else return NW_SUSPEND; } PUBLIC int tcp_read(fd, count) int fd; size_t count; { tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; tcp_fd= &tcp_fd_table[fd]; assert (tcp_fd->tf_flags & TFF_INUSE); if (!(tcp_fd->tf_flags & TFF_CONNECTED)) { reply_thr_put (tcp_fd, ENOTCONN, FALSE); return NW_OK; } tcp_conn= tcp_fd->tf_conn; tcp_fd->tf_flags |= TFF_READ_IP; tcp_fd->tf_read_offset= 0; tcp_fd->tf_read_count= count; assert(tcp_conn->tc_busy == 0); tcp_conn->tc_busy++; tcp_fd_read(tcp_conn, 0); tcp_conn->tc_busy--; if (!(tcp_fd->tf_flags & TFF_READ_IP)) return NW_OK; else return NW_SUSPEND; } /* tcp_restart_connect reply the success or failure of a connect to the user. */ PUBLIC void tcp_restart_connect(tcp_fd) tcp_fd_t *tcp_fd; { tcp_conn_t *tcp_conn; int reply; assert(tcp_fd); assert(tcp_fd->tf_flags & TFF_IOCTL_IP); assert(tcp_fd->tf_ioreq == NWIOTCPLISTEN || tcp_fd->tf_ioreq == NWIOTCPCONN); tcp_conn= tcp_fd->tf_conn; assert(tcp_conn); assert(tcp_conn->tc_connInprogress); if (tcp_conn->tc_state == TCS_CLOSED) { reply= tcp_conn->tc_error; assert(tcp_conn->tc_fd == tcp_fd); tcp_fd->tf_conn= NULL; tcp_conn->tc_fd= NULL; } else { tcp_fd->tf_flags |= TFF_CONNECTED; reply= NW_OK; } tcp_conn->tc_connInprogress= 0; tcp_reply_ioctl (tcp_fd, reply); } /* tcp_close */ PUBLIC void tcp_close(fd) int fd; { tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; tcp_fd= &tcp_fd_table[fd]; assert (tcp_fd->tf_flags & TFF_INUSE); assert (!(tcp_fd->tf_flags & (TFF_IOCTL_IP|TFF_READ_IP|TFF_WRITE_IP))); tcp_fd->tf_flags &= ~TFF_INUSE; if (!tcp_fd->tf_conn) return; tcp_conn= tcp_fd->tf_conn; assert(tcp_conn->tc_fd == tcp_fd); tcp_conn->tc_fd= 0; assert (!tcp_conn->tc_connInprogress); tcp_shutdown (tcp_conn); if (tcp_conn->tc_state == TCS_ESTABLISHED) { tcp_conn->tc_state= TCS_CLOSING; } /* Set the retransmission timeout a bit smaller. */ tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_CLOSING; /* If all data has been acknowledged, close the connection. */ if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT) tcp_close_connection(tcp_conn, ENOTCONN); } PUBLIC int tcp_cancel(fd, which_operation) int fd; int which_operation; { tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; int i; tcp_fd= &tcp_fd_table[fd]; assert (tcp_fd->tf_flags & TFF_INUSE); tcp_conn= tcp_fd->tf_conn; switch (which_operation) { case SR_CANCEL_WRITE: assert (tcp_fd->tf_flags & TFF_WRITE_IP); tcp_fd->tf_flags &= ~TFF_WRITE_IP; if (tcp_fd->tf_write_offset) reply_thr_get (tcp_fd, tcp_fd->tf_write_offset, FALSE); else reply_thr_get (tcp_fd, EINTR, FALSE); break; case SR_CANCEL_READ: assert (tcp_fd->tf_flags & TFF_READ_IP); tcp_fd->tf_flags &= ~TFF_READ_IP; if (tcp_fd->tf_read_offset) reply_thr_put (tcp_fd, tcp_fd->tf_read_offset, FALSE); else reply_thr_put (tcp_fd, EINTR, FALSE); break; case SR_CANCEL_IOCTL: assert (tcp_fd->tf_flags & TFF_IOCTL_IP); tcp_fd->tf_flags &= ~TFF_IOCTL_IP; switch (tcp_fd->tf_ioreq) { case NWIOGTCPCONF: reply_thr_put (tcp_fd, EINTR, TRUE); break; case NWIOSTCPCONF: case NWIOTCPSHUTDOWN: reply_thr_get (tcp_fd, EINTR, TRUE); break; case NWIOTCPCONN: case NWIOTCPLISTEN: assert (tcp_conn->tc_connInprogress); tcp_conn->tc_connInprogress= 0; tcp_conn->tc_fd= 0; tcp_fd->tf_conn= 0; tcp_close_connection(tcp_conn, ENOCONN); reply_thr_get (tcp_fd, EINTR, TRUE); break; default: ip_warning(( "unknown ioctl inprogress: 0x%x", tcp_fd->tf_ioreq )); reply_thr_get (tcp_fd, EINTR, TRUE); break; } break; #if !CRAMPED default: ip_panic(( "unknown cancel request" )); #endif } return NW_OK; } /* tcp_connect */ PRIVATE int tcp_connect(tcp_fd) tcp_fd_t *tcp_fd; { tcp_conn_t *tcp_conn; int state; if (!(tcp_fd->tf_flags & TFF_CONF_SET)) { tcp_reply_ioctl(tcp_fd, EBADMODE); return NW_OK; } if (tcp_fd->tf_flags & TFF_CONNECT) { tcp_reply_ioctl(tcp_fd, EISCONN); return NW_OK; } if ((tcp_fd->tf_tcpconf.nwtc_flags & (NWTC_SET_RA|NWTC_SET_RP)) != (NWTC_SET_RA|NWTC_SET_RP)) { tcp_reply_ioctl(tcp_fd, EBADMODE); return NW_OK; } assert(!tcp_fd->tf_conn); tcp_conn= find_conn_entry(tcp_fd->tf_tcpconf.nwtc_locport, tcp_fd->tf_port->tp_ipaddr, tcp_fd->tf_tcpconf.nwtc_remport, tcp_fd->tf_tcpconf.nwtc_remaddr); if (tcp_conn) { if (tcp_conn->tc_fd) { tcp_reply_ioctl(tcp_fd, EADDRINUSE); return NW_OK; } } else { tcp_conn= find_empty_conn(); if (!tcp_conn) { tcp_reply_ioctl(tcp_fd, EAGAIN); return NW_OK; } } tcp_fd->tf_conn= tcp_conn; return tcp_su4connect(tcp_fd); } /* tcp_su4connect */ PRIVATE int tcp_su4connect(tcp_fd) tcp_fd_t *tcp_fd; { tcp_conn_t *tcp_conn; acc_t *tmp_acc; tcp_conn= tcp_fd->tf_conn; tcp_conn->tc_locport= tcp_fd->tf_tcpconf.nwtc_locport; tcp_conn->tc_locaddr= tcp_fd->tf_port->tp_ipaddr; assert (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP); assert (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA); tcp_conn->tc_remport= tcp_fd->tf_tcpconf.nwtc_remport; tcp_conn->tc_remaddr= tcp_fd->tf_tcpconf.nwtc_remaddr; tcp_setup_conn(tcp_conn); tcp_conn->tc_fd= tcp_fd; tcp_conn->tc_port= tcp_fd->tf_port; tcp_conn->tc_connInprogress= 1; tcp_conn->tc_orglisten= FALSE; tcp_conn->tc_state= TCS_SYN_SENT; tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_CONNECT; /* Start the timer (if necessary) */ tcp_set_send_timer(tcp_conn); tcp_conn_write(tcp_conn, 0); if (tcp_conn->tc_connInprogress) return NW_SUSPEND; else return NW_OK; } PRIVATE int conn_right4fd(tcp_conn, tcp_fd) tcp_fd_t *tcp_fd; tcp_conn_t *tcp_conn; { unsigned long flags; flags= tcp_fd->tf_tcpconf.nwtc_flags; if (tcp_fd->tf_tcpconf.nwtc_locport != tcp_conn->tc_locport) return FALSE; if ((flags & NWTC_SET_RA) && tcp_fd->tf_tcpconf.nwtc_remaddr != tcp_conn->tc_remaddr) return FALSE; if ((flags & NWTC_SET_RP) && tcp_fd->tf_tcpconf.nwtc_remport != tcp_conn->tc_remport) return FALSE; if (tcp_fd->tf_port != tcp_conn->tc_port) return FALSE; return TRUE; } /* tcp_listen */ PRIVATE int tcp_listen(tcp_fd) tcp_fd_t *tcp_fd; { tcp_conn_t *tcp_conn; int state; if (!(tcp_fd->tf_flags & TFF_CONF_SET)) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EBADMODE, TRUE); return NW_OK; } if (tcp_fd->tf_flags & TFF_CONNECT) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get(tcp_fd, EISCONN, TRUE); return NW_OK; } tcp_conn= tcp_fd->tf_conn; assert(!tcp_conn); if ((tcp_fd->tf_tcpconf.nwtc_flags & (NWTC_SET_RA|NWTC_SET_RP)) == (NWTC_SET_RA|NWTC_SET_RP)) { tcp_conn= find_conn_entry( tcp_fd->tf_tcpconf.nwtc_locport, tcp_fd->tf_port->tp_ipaddr, tcp_fd->tf_tcpconf.nwtc_remport, tcp_fd->tf_tcpconf.nwtc_remaddr); if (tcp_conn) { if (tcp_conn->tc_fd) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, EADDRINUSE, TRUE); return NW_OK; } tcp_fd->tf_conn= tcp_conn; return tcp_su4listen(tcp_fd); } } tcp_conn= find_empty_conn(); if (!tcp_conn) { tcp_fd->tf_flags &= ~TFF_IOCTL_IP; reply_thr_get (tcp_fd, EAGAIN, TRUE); return NW_OK; } tcp_fd->tf_conn= tcp_conn; return tcp_su4listen(tcp_fd); } PRIVATE void tcp_buffree (priority) int priority; { int i; tcp_conn_t *tcp_conn; if (priority == TCP_PRI_FRAG2SEND) { for (i=0, tcp_conn= tcp_conn_table; itc_flags & TCF_INUSE)) continue; if (!tcp_conn->tc_frag2send) continue; if (tcp_conn->tc_busy) continue; bf_afree(tcp_conn->tc_frag2send); tcp_conn->tc_frag2send= 0; } } if (priority == TCP_PRI_CONN_EXTRA) { for (i=0, tcp_conn= tcp_conn_table; itc_flags & TCF_INUSE)) continue; if (tcp_conn->tc_busy) continue; if (tcp_conn->tc_adv_data) { bf_afree(tcp_conn->tc_adv_data); tcp_conn->tc_adv_data= NULL; } } } if (priority == TCP_PRI_CONNwoUSER) { for (i=0, tcp_conn= tcp_conn_table; itc_flags & TCF_INUSE)) continue; if (tcp_conn->tc_busy) continue; if (tcp_conn->tc_fd) continue; if (tcp_conn->tc_state == TCS_CLOSED) continue; if (tcp_conn->tc_rcvd_data == NULL && tcp_conn->tc_send_data == NULL) { continue; } tcp_close_connection (tcp_conn, EOUTOFBUFS); } } if (priority == TCP_PRI_CONN_INUSE) { for (i=0, tcp_conn= tcp_conn_table; itc_flags & TCF_INUSE)) continue; if (tcp_conn->tc_busy) continue; if (tcp_conn->tc_state == TCS_CLOSED) continue; if (tcp_conn->tc_rcvd_data == NULL && tcp_conn->tc_send_data == NULL) { continue; } tcp_close_connection (tcp_conn, EOUTOFBUFS); } } } #ifdef BUF_CONSISTENCY_CHECK PRIVATE void tcp_bufcheck() { int i; tcp_conn_t *tcp_conn; tcp_port_t *tcp_port; for (i= 0, tcp_port= tcp_port_table; itp_pack) bf_check_acc(tcp_port->tp_pack); } for (i= 0, tcp_conn= tcp_conn_table; itc_busy); if (tcp_conn->tc_rcvd_data) bf_check_acc(tcp_conn->tc_rcvd_data); if (tcp_conn->tc_adv_data) bf_check_acc(tcp_conn->tc_adv_data); if (tcp_conn->tc_send_data) bf_check_acc(tcp_conn->tc_send_data); if (tcp_conn->tc_remipopt) bf_check_acc(tcp_conn->tc_remipopt); if (tcp_conn->tc_tcpopt) bf_check_acc(tcp_conn->tc_tcpopt); if (tcp_conn->tc_frag2send) bf_check_acc(tcp_conn->tc_frag2send); } } #endif PUBLIC void tcp_notreach(tcp_conn) tcp_conn_t *tcp_conn; { int new_ttl; new_ttl= tcp_conn->tc_ttl; if (new_ttl == IP_MAX_TTL) { if (tcp_conn->tc_state == TCS_SYN_SENT) tcp_close_connection(tcp_conn, EDSTNOTRCH); return; } else if (new_ttl == TCP_DEF_TTL) new_ttl= TCP_DEF_TTL_NEXT; else { new_ttl *= 2; if (new_ttl> IP_MAX_TTL) new_ttl= IP_MAX_TTL; } tcp_conn->tc_ttl= new_ttl; tcp_conn->tc_stt= 0; tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_UNA; tcp_conn_write(tcp_conn, 1); } /* tcp_setup_conn */ PRIVATE void tcp_setup_conn(tcp_conn) tcp_conn_t *tcp_conn; { assert(!tcp_conn->tc_connInprogress); if (tcp_conn->tc_flags & TCF_INUSE) { assert (tcp_conn->tc_state == TCS_CLOSED); assert (!tcp_conn->tc_send_data); if (tcp_conn->tc_senddis < get_time()) tcp_conn->tc_ISS= 0; } else { assert(!tcp_conn->tc_busy); tcp_conn->tc_senddis= 0; tcp_conn->tc_ISS= 0; tcp_conn->tc_tos= TCP_DEF_TOS; tcp_conn->tc_ttl= TCP_DEF_TTL; tcp_conn->tc_rcv_wnd= TCP_MAX_RCV_WND_SIZE; tcp_conn->tc_fd= NULL; } if (!tcp_conn->tc_ISS) { tcp_conn->tc_ISS= (get_time()/HZ)*ISS_INC_FREQ; } tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS; tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS+1; tcp_conn->tc_SND_UP= tcp_conn->tc_ISS; tcp_conn->tc_SND_PSH= tcp_conn->tc_ISS; tcp_conn->tc_IRS= 0; tcp_conn->tc_RCV_LO= tcp_conn->tc_IRS; tcp_conn->tc_RCV_NXT= tcp_conn->tc_IRS; tcp_conn->tc_RCV_HI= tcp_conn->tc_IRS; tcp_conn->tc_RCV_UP= tcp_conn->tc_IRS; assert(tcp_conn->tc_rcvd_data == NULL); assert(tcp_conn->tc_adv_data == NULL); assert(tcp_conn->tc_send_data == NULL); tcp_conn->tc_remipopt= NULL; tcp_conn->tc_tcpopt= NULL; assert(tcp_conn->tc_frag2send == NULL); tcp_conn->tc_stt= 0; tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; tcp_conn->tc_0wnd_to= 0; tcp_conn->tc_rtt= TCP_DEF_RTT; tcp_conn->tc_mss= TCP_DEF_MSS; tcp_conn->tc_error= NW_OK; tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA + 2*tcp_conn->tc_mss; tcp_conn->tc_snd_cthresh= TCP_MAX_SND_WND_SIZE; tcp_conn->tc_snd_cinc= (long)TCP_DEF_MSS*TCP_DEF_MSS/TCP_MAX_SND_WND_SIZE+1; tcp_conn->tc_snd_wnd= TCP_MAX_SND_WND_SIZE; tcp_conn->tc_rt_time= 0; tcp_conn->tc_rt_seq= 0; tcp_conn->tc_rt_threshold= tcp_conn->tc_ISS; tcp_conn->tc_flags= TCF_INUSE; clck_untimer(&tcp_conn->tc_transmit_timer); tcp_conn->tc_transmit_seq= 0; } /* * $PchId: tcp.c,v 1.14.2.2 1999/11/17 22:05:27 philip Exp $ */