RIGHT:''&counter(all); - &counter(yesterday); - &counter(today); - &online;'' CENTER:&size(28){&color(darkgreen){''Mesのソース解析''};}; #br ---- #contents ---- **power onからのソース解析 [#jef2af35] -power onプロセスの書いてあるところ(30xxcrt0.S) setup stack pointer move initial data from ROM to RAM clear non-initial RAM area call main routine -main routineの書いてあるところ(main.c) set config (bus width etc.) init_device(); if MODE != 5 then user1_start else user2_start (MES/main.c) -user2_startの書いてあるところ(MES/main.c) init(); device open, set_handle(task_sw) ip_init(); arp_init(); udp_init(); tcp_init(); task_init(); task_attach(initial()); **TRAPAからのソース解析 [#l33c6a7c] -trapaが書いてあるところ(handle_h8.s) .h8300h .section .text .align 1 .global _int_wovi .global _int_trap0 .global _int_trap1 .global _int_trap2 _int_wovi: _int_trap0: push.l er0 いろいろPUSH push.l er1 push.l er2 push.l er3 push.l er4 push.l er5 push.l er6 mov.l @_stackpp,er0 SPをstackppに設定 mov.l sp,@er0 mov.l @_handlefunc,er0 handlefuncをcall jsr @er0 _int_trap2: mov.l @_stackpp,er0 SPをstackppに設定 mov.l @er0,sp pop.l er6 いろいろPOP pop.l er5 pop.l er4 pop.l er3 pop.l er2 pop.l er1 pop.l er0 rte リターン _int_trap1: mov.l @_curtaskp,er0 curtaskpをer0に入れて mov.l @er0,er0 rte リターン .end -handlefunc, stackpp, curtaskpが書いてあるところ(set_handle.c) int (*handlefunc)(); void *stackpp, *curtaskp; void set_handle(int (*func)(), void *spp, void *ctp) { handlefunc = func; stackpp = spp; curtaskp = ctp; } -set_handle()を呼んでいるところ(MES/main.c) set_handle(task_sw, &stackptr, &curtask); つまり、curtaskにtask_sw()を登録してから、 task_init()を呼ぶ(initial()が呼ばれる) **get_ipinfo()とget_ip() [#v09e8801] 単純に自分のIP, MASK, MACを返すだけ **sock構造体の状態変数の推移 [#aee608d8] -sock構造体(本名tcp_info) typedef struct { uint32_t rem_ip; // Remote IP address uint32_t send_unacked; uint32_t send_next; uint32_t receive_next; uint32_t packet_size; uint32_t next_packet_size; uint32_t recv_packet_size; STRING packet; STRING next_packet; STRING recv_packet; int32_t (*event_listener)(uint32_t, Byte, uint32_t, uint16_t); uint16_t remport; // Remote TCP port uint16_t locport; // Local TCP port uint16_t id; // task id uint16_t send_mtu; Byte state; Byte type; Byte flags; // State machine flags Byte myflags; // My flags to be Txed Byte close_count; Byte resend_timer; Byte ack_timer; Byte fin_timer; } tcp_info; -状態変数の種類 #define TCP_STATE_FREE 1 #define TCP_STATE_RESERVED 2 #define TCP_STATE_CLOSED 3 #define TCP_STATE_LISTENING 4 #define TCP_STATE_SYN_RECEIVED 5 #define TCP_STATE_SYN_SENT 6 #define TCP_STATE_FINW1 7 #define TCP_STATE_FINW2 8 #define TCP_STATE_CLOSING 9 #define TCP_STATE_LAST_ACK 10 #define TCP_STATE_TIMED_WAIT 11 #define TCP_STATE_CONNECTED 12 -まず初期化(MES/tcp.c) tcp_socket配列は、static tcp_infoで宣言されている。 void tcp_init() { int32_t i; for(i = 0;i <= TCP_NUM;i++) { tcp_socket[i].state = TCP_STATE_FREE; tcp_socket[i].type = TCP_TYPE_NONE; tcp_socket[i].flags = 0; tcp_socket[i].rem_ip = 0; tcp_socket[i].remport = 0; tcp_socket[i].locport = 0; tcp_socket[i].myflags = 0; tcp_socket[i].send_mtu = TCP_DEF_MTU; tcp_socket[i].event_listener = 0; } } -tcpsocketからCALLされるtcp_getsocketでTCP_STATE_RESERVEDになる -tcpconnectでtcp_newstate(sock, TCP_STATE_SYN_SENT)される -tcpのcase TCP_STATE_SYN_SENT:内でSYN+ACKを検出したらTCP_STATE_CONNECTEDになる case TCP_STATE_SYN_SENT: if(tcphdr->hlen_flags & TCP_FLAG_RESET) { soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } /* Is it SYN+ACK? */ if((tcphdr->hlen_flags & TCP_FLAG_SYN) && (tcphdr->hlen_flags & TCP_FLAG_ACK)) { /* Right ACK? */ if( tcphdr->ackno != soc->send_next ) return -1; soc->receive_next = tcphdr->seqno; soc->receive_next++; /* ACK SYN */ soc->send_unacked = soc->send_next; tcp_newstate(sock, TCP_STATE_CONNECTED); soc->myflags = TCP_FLAG_ACK; tcp_sendcontrol(sock); プライベート内では通るけど、ルータ越しでは通らない soc->event_listener(sock, TCP_EVENT_CONNECTED, soc->rem_ip, soc->remport); return 0; --結局、ルータ越しIPではtcp()に飛んで来ないのか?~ tcp()の呼び先であるip.c内のtcpip()を見ると・・・ int32_t tcpip(uint32_t fd, IP_HDR *iphdr, Byte *destmac) { int32_t index, ret; uint32_t ip, mask; Byte mac[6]; if(iphdr->version != 4) return -1; index = fd2index(fd); if(index == -1) return -1; ret = set_ip_addr(index, iphdr->destip, destmac); if(ret == -1) return -1; ret = get_ipinfo(index, &ip, &mask, mac); if(ret == -1) return -1; if((ip & mask) != (iphdr->srcip & mask)) return -1; ここで同一ネットワーク内でないとreturnしている? if(ip != iphdr->destip && (iphdr->destip | mask) != 0xffffffff) return -1; switch(iphdr->proto) { case ICMP: icmp(fd, iphdr); break; case UDP: udp(fd, iphdr); break; case TCP: tcp(fd, iphdr); break; } return 0; --コメントアウトしたら、相手先のSYNが来なくなった。。。で、元に戻してもSYNが来ない。。。困った --(この辺にルータ越しのバグが潜んでいるかも) -つぎにディスコネクトの推移 -まずはtcpfreeでtcp_closeとtcp_releasesocketとが呼ばれる -tcp_closeの処理はNAKデータ有り無しで変わる --NAKなし・・・FINパケットを送出してtcp_newstate(sock, TCP_STATE_FINW1)する --NAKあり・・・soc->flags|=TCP_INTFLAGS_CLOSEPENDINGする -tcp_releasesocketでは、以下のように-1でreturn if((soc->id != id) && (soc->state != TCP_STATE_FREE) && (soc->state != TCP_STATE_RESERVED) && (soc->state != TCP_STATE_CLOSED)) { return -1; } -そうすると、tcpfreeでは、task->state |= NET_STATEしてreturn ret = tcp_releasesocket(id, sock); if(ret == -1) { task = get_taskptr(id); task->state |= NET_STATE; soc->fin_timer = 2; } return ret; -で、task.cのinitial()では、下記のようにあるので、tcp_interval()が定期的に呼ばれている。 if(sys_count != sys_count_ms / 1000) { check_flagroot_index(); tcp_interval(); } -tcp_interval()では、下記のように処理している。 if((soc->fin_timer) > 0) { if(--(soc->fin_timer) == 0) { task->state &= ~NET_STATE; task->retval = -1; tcp_abort(i); tcp_releasesocket(soc->id, i); } } -tcp_abort()を見ると下記のように処理している。 case TCP_STATE_SYN_SENT: case TCP_STATE_SYN_RECEIVED: case TCP_STATE_CONNECTED: case TCP_STATE_FINW1: case TCP_STATE_FINW2: case TCP_STATE_CLOSING: case TCP_STATE_LAST_ACK: soc->myflags = TCP_FLAG_RESET; tcp_sendcontrol(sock); tcp_newstate(sock, TCP_STATE_CLOSED); return sock; -この辺にACKが返らないバグがありそう。 -TCP_STATE_FINW1からの処理はtcp()に記述がある。 case TCP_STATE_FINW1: //sockの状態変数はTCP_STATE_FINW1になっている if(tcphdr->hlen_flags & TCP_FLAG_RESET) { //結局、ここを通っていない。ただし、tcpip()からtcp()は呼ばれている soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } /* Is it FIN+ACK? */ if((tcphdr->hlen_flags & TCP_FLAG_FIN) && (tcphdr->hlen_flags & TCP_FLAG_ACK)) { if(tcphdr->ackno != soc->send_next) return -1; soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; soc->send_unacked = soc->send_next; tcp_newstate(sock, TCP_STATE_TIMED_WAIT); soc->myflags = TCP_FLAG_ACK; tcp_sendcontrol(sock); return 0; } /* Is it just FIN */ if(tcphdr->hlen_flags & TCP_FLAG_FIN) { soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; tcp_newstate(sock, TCP_STATE_CLOSING); soc->myflags = TCP_FLAG_ACK; tcp_sendcontrol(sock); return 0; } /* Is it just ACK? */ //tcpdumpの結果では、ここを通るはず if(tcphdr->hlen_flags & TCP_FLAG_ACK) { /* Right ACK? */ //このif文の中は通っていない。このcase文を通るか? if( tcphdr->ackno != soc->send_next ) return -1; soc->send_unacked = soc->send_next; tcp_newstate(sock, TCP_STATE_FINW2); //TCP_STATE_FINW2に状態遷移 return 0; } break; case TCP_STATE_FINW2: //で、TCP_STATE_FINW2の処理を見ると if(tcphdr->hlen_flags & TCP_FLAG_RESET) { soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } if(tcphdr->hlen_flags & TCP_FLAG_FIN) { //tcpdumpの結果では、ここを通るはず soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; tcp_newstate(sock, TCP_STATE_TIMED_WAIT); //TCP_STATE_TIMED_WAITに状態遷移 soc->myflags = TCP_FLAG_ACK; //ここでACK処理している? tcp_sendcontrol(sock); return 0; } break; case TCP_STATE_CLOSING: if(tcphdr->hlen_flags & TCP_FLAG_RESET) { soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } /* Is it ACK? */ if( tcphdr->hlen_flags & TCP_FLAG_ACK ) { /* Right ACK? */ if( tcphdr->ackno != soc->send_next ) return -1; soc->send_unacked = soc->send_next; tcp_newstate(sock, TCP_STATE_TIMED_WAIT); return 0; } if(tcphdr->hlen_flags & TCP_FLAG_FIN) { soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; soc->myflags = TCP_FLAG_ACK; tcp_sendcontrol(sock); return 0; } break; case TCP_STATE_LAST_ACK: if(tcphdr->hlen_flags & TCP_FLAG_RESET) { soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } /* Is it ACK? */ if( tcphdr->hlen_flags & TCP_FLAG_ACK ) { /* Right ACK? */ if(tcphdr->ackno != soc->send_next) return -1; soc->send_unacked = soc->send_next; if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return 0; } /* Is it repeated FIN? */ if(tcphdr->hlen_flags & TCP_FLAG_FIN) { /* ACK FIN and all data */ soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; soc->myflags = TCP_FLAG_FIN | TCP_FLAG_ACK; tcp_sendcontrol(sock); return 0; } break; case TCP_STATE_TIMED_WAIT: if(tcphdr->hlen_flags & TCP_FLAG_RESET) { soc->event_listener(sock, TCP_EVENT_ABORT, soc->rem_ip, soc->remport); if(soc->type & TCP_TYPE_SERVER) tcp_newstate(sock, TCP_STATE_LISTENING); else tcp_newstate(sock, TCP_STATE_CLOSED); return -1; } /* Is it repeated FIN? */ if(tcphdr->hlen_flags & TCP_FLAG_FIN) { soc->receive_next = tcphdr->seqno; soc->receive_next++; soc->receive_next += dlen; soc->myflags = TCP_FLAG_ACK; tcp_sendcontrol(sock); return 0; } break; -TCPクローズ時、FINを送出してTCP_STATE_FINW1になるが、~ 次のバケット(相手先からのACK)に対して、下記のどこかでreturnしている~ (すなわち、TCP_STATE_FINW1の処理が行われていない)~ 下記はtcp()の冒頭部分 ptr = (STRING)iphdr; ptr = &(ptr[iphdr->header_len * 4]); tcphdr = (TCP_HDR*)ptr; len = iphdr->datasize; len -= (int32_t)iphdr->header_len * 4; hlen = tcphdr->hlen_flags & 0xF000; hlen >>= 10; if(hlen < MIN_TCP_HLEN) return -1; olen = hlen - MIN_TCP_HLEN; if(olen > MAX_TCP_OPTLEN) return -1; if(hlen > len) return -1; dlen = len - hlen; tcpdata = &ptr[hlen]; sock = tcp_mapsocket(iphdr); //ここで-1が返される if(sock == -1) return 0; //TCP_STATE_FINW1が処理されない soc = &tcp_socket[sock]; -tcp_mapsocketを見ると、下記の通り。 static int32_t tcp_mapsocket(IP_HDR *iphdr) { TCP_HDR *tcphdr; STRING ptr; int32_t i; ptr = (STRING)iphdr; ptr = &(ptr[iphdr->header_len * 4]); tcphdr = (TCP_HDR*)ptr; for(i = 0;i < TCP_NUM;i++) { //既存ソケットの検索 if(tcp_socket[i].state == TCP_STATE_LISTENING) continue; if(tcp_socket[i].remport != tcphdr->sport) continue; //このif文がFALSEにならない=remportとsportとが違う if(tcp_socket[i].locport != tcphdr->dport) continue; if(tcp_socket[i].rem_ip != iphdr->srcip) continue; return i; } if((tcphdr->hlen_flags & TCP_FLAG_SYN) == 0) return -1; //SYN受信〜新規ソケットの作成 if(tcphdr->hlen_flags & TCP_FLAG_ACK) return -1; if(tcphdr->hlen_flags & TCP_FLAG_RESET) return -1; if(tcphdr->hlen_flags & TCP_FLAG_FIN) return -1; for(i = 0;i < TCP_NUM;i++){ if(tcp_socket[i].state != TCP_STATE_LISTENING) continue; if(tcp_socket[i].locport != tcphdr->dport) continue; tcp_socket[i].rem_ip = iphdr->srcip; tcp_socket[i].remport = tcphdr->sport; return i; } return -1; } --TCP_STATE_FINW1のとき、既存ソケットの検索に失敗している? --if(tcp_socket[i].remport != tcphdr->sport) continue; //このif文がFALSEにならない=remportとsportとが違う --どうして? --調べると、remportもlocportもrem_ipも一致していない。~ すなわち、受信パケットのポインタが壊れていると考えられる --SYN受信時にtcp_socket[i].rem_ipとremportが更新されていない可能性もある。 **initial()の解析 [#p50c1481] -MES/task.cにあるinitial()が肝心。 ひとまずソース全体を張っておく。 void initial() { static STRING ptrs[2]; static Byte name[16]; static int64_t sys_count_ms; volatile char w; Task *taskptr; int32_t c, *sp_ptr; strcpy(name, "/rom0/shell"); ptrs[0] = name; ptrs[1] = 0; curtask->req = EXEC_REQ; curtask->arg[0] = 1; curtask->arg[1] = (int32_t)ptrs; request(curtask); if(curtask->retval == -1) { strcpy(name, "/rom0/netsh"); ptrs[0] = name; ptrs[1] = 0; curtask->req = EXEC_REQ; curtask->arg[0] = 1; curtask->arg[1] = (int32_t)ptrs; request(curtask); if(curtask->retval == -1) { write_device(stdio, "Shell not found, system halt.\n", 31); while(1); } } sys_count_ms = 0; while(1) { //無限ループ int_disable(); for(taskptr = curtask->next;taskptr != curtask;taskptr = taskptr->next) { //タスクのスキャン if(taskptr->state & EXIT_STATE) { //タスクの終了処理 dettach_task(taskptr); taskptr = curtask->next; continue; } if(taskptr->state & REQ_STATE) request(taskptr); //要求待ちタスクの実行 if(taskptr->net_count > 0) { (taskptr->net_count)--; net_request(taskptr); //SEND_ARP_REQのみを処理してる } if(taskptr->count > 0) { taskptr->count -= time_count / counter; if(taskptr->count <= 0) taskptr->count = 0; } if(taskptr->interval > 0) { taskptr->interval -= time_count / counter; if(taskptr->interval <= 0) { #ifdef H8_300H sp_ptr = taskptr->sp; sp_ptr = &(sp_ptr[-1]); taskptr->sp = sp_ptr; for(c = 0;c < PC_REG;c++) sp_ptr[c] = sp_ptr[c + 1]; #endif #ifdef SH sp_ptr[PR_REG] = sp_ptr[PC_REG]; #endif sp_ptr[PC_REG] = (int32_t)(taskptr->sigfunc); taskptr->interval = 0; } } sys_count_ms += time_count / counter; } ip_request(); //rcv_fifoからパケットを取出して time_count -= (time_count / counter) * counter; //case ARP_TYPE, case IP_TYPEする if(sys_count != sys_count_ms / 1000) { check_flagroot_index(); tcp_interval(); //相手のFINに対するACK不送出に関連がある? } sys_count = sys_count_ms / 1000; int_enable(); TRAP0; } } **TCPクライアントのサンプル [#d60cef52] -shell.cのコマンドでdiscardを加筆してます。 --TCPポート7(ECHO)を開こうとします。 --"q"を押すとクローズします。 -daytimeもあるけど、socketのopen, closeのみの検証ではdiscardがgood --doscardコマンドは実はechoコマンドが正しいけど、echoコマンドは既出。 **timer周りの変更点 [#c0bfa57f] FDAUのクロックは20MHzなので、task.cの以下を変更する。 でも、ちょっと変なソースなので再考したい。 static void init_timer() { time_count = 0; counter = ((25 * 1000) >> 8) & 0xff; ここの25を20に変更 counter = (256 - counter) & 0xff; WDTはアップカウンタなので256から引算 start_timer(); } -どうしてmagic numberなんだろう? -counterを分母として減算している場所があるけど、変ではないか?