ここでは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.ctcp関数で設定します。

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.htcp関数の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状態の実装

戻る