ここではH8OS(ver3.51)に対してTCP再送機能を強化するための改造内容をまとめています。
さらに詳細な内容については次のページを参照してください。
|
|
【シーケンス番号のAPLへの通知】
改造範囲をできるだけ抑えるためにシーケンス番号は構造体sockaddr_inに追加することにしました。この構造体は、
tcpip.hとAPLから参照するsyscall.hの2カ所に定義されています。
struct sockaddr_in {
unsigned char sin_len; /* 構造体の大きさ */
unsigned char sin_familly; /* PF_INET */
unsigned short sin_port; /* ポート番号 */
unsigned int sin_addr; /* アドレス */
unsigned int seq; /* シーケンス番号 */
unsigned int ack; /* ACK番号 */ };
|
このデータはシステムコールのtcpsocketで登録する割り込みハンドラの第三パラメータで引き継がれます。
また、このメンバへのシーケンス番号は、次のようにtcp.cのtcp関数で設定します。
int tcp(ip_header *ip) {
// 中略
tcp_header tcp;
struct sockaddr_in addr;
// 中略
ether_read((char*)&tcp, sizeof(ether_header) + header_size, sizeof(tcp_header));
// 中略
tcp_header_size = (tcp.header_size >> 2) & 0x3c;
tcp_data_size = data_size = ip->data_size - header_size - tcp_header_size;
addr.seq = tcp.src_seq ;
addr.ack = tcp.dest_seq ;
|
上記のメンバ追加により利用するRAM領域が広くなるので、メモリ割付も拡大する必要があります。デフォルトのsys3068.xではmake時に
region ram is full (kern3068.coff section .bss)
というエラーが発生しましたので、次のように赤字部分を修正しました。
|
MEMORY
{
vectors(r) : o = 0x000000, l = 0x000100
syscall(r) : o = 0x000100, l = 0x000200
rom(rx) : o = 0x000300, l = 0x00cf00
user(r) : o = 0x00d200, l = 0x002e00
ram(rwx) : o = 0xffc720, l = 0x001f00
stack(rw) : o = 0xffff0c, l = 0x000004
}
|
|
|
【コネクション確立時のSYN+ACK信号紛失対応】
tcp.hのtcp関数のrmseqの設定がインクリメント処理になっている部分を受信したシーケンス番号に1を加えるように修正します。具体的なシーケンスはこちら。
if(tcp.flag & SYN)tip->rmseq = tcp.src_seq + 1 ; // tip->rmseq++;
|
|
また、SYN+ACKが紛失した場合には、クライアントはSYNを再送してきますが、サーバ(H8OS-APL)はすでにtcpsocketでH8OSに関数(tcprecv)を登録し、SNYRCVD状態ですので、その状態でSYN+ACKを再送する必要があります。そこで、tcpsocket関数のACK返送部分をSNYRCVD状態の処理にも記述しました。下記でコメントアウトしてるのは、すでにconnect構造体(tipのさす先の領域)は設定されているので、その部分への再設定をやめた部分です。
|
switch(tip->status) {
// 中略
case TCP_SYNRCVD:
if(tcp.flag & SYN){
// if((mac = query_mac(passive_destaddr.sin_addr)) == 0) return -1;
tip->fActive = 0;
// tip->rmseq = seq + 1;
tip->status = TCP_SYNRCVD;
// tip->destaddr = passive_destaddr;
// tip->destport = passive_destport;
flag = SYN | ACK;
// tip->myseq = sequence;
tip->time = -1;
// tip->index = index;
set_ether_header(mac, mytable.mac, IP_TYPE);
myip = set_ip_header(IP_TCP, tip->destaddr.sin_addr, 20, 28, 0);
set_tcp_header(flag, tip->destaddr.sin_port, tip->destport, tip, myip, 0);
ether_flush(sizeof(ether_header) + 20 + 28);
break;
}
else
if(tcp.flag & PSH) {
|
|
【コネクション確立時のACK信号紛失対応】
オリジナルのソースではACK信号を受信してTCP_ESTAB状態に遷移してからでないとデータ受信の処理が実行できません。
そこで、tcp関数においてデータを受信したらTCP_ESTABに遷移すると共にデータ受信処理を実行するように修正しました。データの受信にはPSHフラグをチェックする方法もありますが、データ受信時にPHSフラグが設定されていないケースもあるので、データサイズで判定することにしました。if文中にbreakの記載がないのは漏れではなく、そのままTCP_ESTABの処理を実行するためです。具体的なシーケンスはこちら。
switch(tip->status) {
// 中略
case TCP_SYNRCVD:
if(data_size > 0) {
tip->status = TCP_ESTAB;
}
else if(tcp.flag & ACK) {
tip->status = TCP_ESTAB;
// 中略
break ;
}
else{
break;
};
case TCP_ESTAB:
|
|
|
【ACK番号設定の改造】
H8OS(ver3.51)ではACK番号にH8OSで記憶してインクリメントした値を設定するため、再送パケットを受けてもインクリメントしてしまい、再送パケットに対して正しく応答できません。そこで、受信したパケットのシーケンス番号を元に、ACK番号を設定するように改造しました。H8OSのTCPの処理やシーケンスのケーススタディなど詳細はこちら。
case TCP_ESTAB:
// 中略
// tip->rmseq += data_size;
tip->rmseq = tcp.src_seq + data_size ;
|
|
|
【コネクションクローズの改善】
H8OS(ver3.51)ではコネクションクローズの処理で信号紛失が生じたケースについては考慮されていません。これに対応するため次のような処理を追加しました。元のコードを大幅に改修したので具体的な修正内容はソースをご覧ください(ソースはテスト後掲載予定)
・FIN再送(タイマ機能の追加)
・信号紛失や信号交差時の状態考慮
・TIMEWAIT状態やCLOSING状態の実装
|
|
戻る
|