Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. ;-----------------------------------------------------------------
  2. ;
  3. ; TCP_output
  4. ;
  5. ; IN:  eax = socket pointer
  6. ;
  7. ; OUT: /
  8. ;
  9. ;-----------------------------------------------------------------
  10. align 4
  11. TCP_output:
  12.  
  13.         DEBUGF 1,"TCP_output, socket: %x\n", eax
  14.  
  15.  
  16. ; We'll detect the length of the data to be transmitted, and flags to be used
  17. ; If there is some data, or any critical controls to send (SYN / RST), then transmit
  18. ; Otherwise, investigate further
  19.  
  20.         mov     ebx, [eax + TCP_SOCKET.SND_MAX]
  21.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  22.         jne     .not_idle
  23.  
  24.         mov     ebx, [eax + TCP_SOCKET.t_idle]
  25.         cmp     ebx, [eax + TCP_SOCKET.t_rxtcur]
  26.         jle     .not_idle
  27.  
  28. ; We have been idle for a while and no ACKS are expected to clock out any data we send..
  29. ; Slow start to get ack "clock" running again.
  30.  
  31.         mov     ebx, [eax + TCP_SOCKET.t_maxseg]
  32.         mov     [eax + TCP_SOCKET.SND_CWND], ebx
  33.  
  34.   .not_idle:
  35.   .again:
  36.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]         ; calculate offset
  37.         sub     ebx, [eax + TCP_SOCKET.SND_UNA]         ;
  38.  
  39.         mov     ecx, [eax + TCP_SOCKET.SND_WND]         ; determine window
  40.         cmp     ecx, [eax + TCP_SOCKET.SND_CWND]        ;
  41.         jl      @f                                      ;
  42.         mov     ecx, [eax + TCP_SOCKET.SND_CWND]        ;
  43.        @@:                                              ;
  44.  
  45.         call    TCP_outflags    ; in dl
  46.  
  47. ; If in persist timeout with window of 0, send 1 byte.
  48. ; Otherwise, if window is small but nonzero, and timer expired,
  49. ; we will send what we can and go to transmit state
  50.  
  51.         test    [eax + TCP_SOCKET.t_force], -1
  52.         jz      .no_persist_timeout
  53.  
  54.         test    ecx, ecx
  55.         jnz     .no_zero_window
  56.  
  57.         cmp     ebx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
  58.         jge     @f
  59.  
  60.         and     dl, not (TH_FIN)          ; clear the FIN flag    ??? how can it be set before?
  61.  
  62.        @@:
  63.         inc     ecx
  64.         jmp     .no_persist_timeout
  65.  
  66.   .no_zero_window:
  67.  
  68.         mov     [eax + TCP_SOCKET.timer_persist], 0
  69.         mov     [eax + TCP_SOCKET.t_rxtshift], 0
  70.  
  71.   .no_persist_timeout:
  72.  
  73. ;;;106
  74.  
  75.         mov     esi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
  76.         cmp     esi, ecx
  77.         jl      @f
  78.         mov     esi, ecx
  79.        @@:
  80.         sub     esi, ebx
  81.  
  82.         cmp     esi, -1
  83.         jne     .not_minus_one
  84.  
  85. ; If FIN has been set, but not ACKed, and we havent been called to retransmit,
  86. ; len (esi) will be -1
  87. ; Otherwise, window shrank after we sent into it.
  88. ; If window shrank to 0, cancel pending retransmit and pull SND_NXT back to (closed) window
  89. ; We will enter persist state below.
  90. ; If window didn't close completely, just wait for an ACK
  91.  
  92.         xor     esi, esi
  93.  
  94.         test    ecx, ecx
  95.         jnz     @f
  96.  
  97.         mov     [eax + TCP_SOCKET.timer_retransmission], 0   ; cancel retransmit
  98.  
  99.         push    [eax + TCP_SOCKET.SND_UNA]
  100.         pop     [eax + TCP_SOCKET.SND_NXT]
  101.        @@:
  102.  
  103.   .not_minus_one:
  104.  
  105. ;;; 124
  106.  
  107.         cmp     esi, [eax + TCP_SOCKET.t_maxseg]
  108.         jle     @f
  109.  
  110.         mov     esi, [eax + TCP_SOCKET.t_maxseg]
  111.         ;sendalot = 1
  112.  
  113.        @@:
  114.  
  115. ;;; 128
  116.  
  117.         mov     edi, [eax + TCP_SOCKET.SND_NXT]
  118.         add     edi, esi        ; len
  119.         sub     edi, [eax + TCP_SOCKET.SND_UNA]
  120.         add     edi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
  121.         cmp     edi, 0
  122.         jle     @f
  123.  
  124.         and     dl, not (TH_FIN)          ; clear the FIN flag
  125.  
  126.        @@:
  127.  
  128.  
  129. ; set ecx to space available in receive buffer
  130. ; From now on, ecx will be the window we advertise to the other end
  131.  
  132.         mov     ecx, SOCKET_MAXDATA
  133.         sub     ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size]
  134.  
  135. ;------------------------------
  136. ; Sender silly window avoidance
  137.  
  138.         cmp     ecx, [eax + TCP_SOCKET.t_maxseg]
  139.         je      .send
  140.  
  141. ;;; TODO: 144-145
  142.  
  143.         test    [eax + TCP_SOCKET.t_force], -1
  144.         jnz     .send
  145.  
  146.         mov     ebx, [eax + TCP_SOCKET.max_sndwnd]
  147.         shr     ebx, 1
  148.         cmp     ecx, ebx
  149.         jge     .send
  150.  
  151.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]
  152.         cmp     ebx, [eax + TCP_SOCKET.SND_MAX]
  153.         jl      .send
  154.  
  155. ;----------------------------------------
  156. ; Check if a window update should be sent
  157.  
  158.         test    ecx, ecx        ; window
  159.         jz      .no_window
  160.  
  161. ;;; TODO 154-172
  162.  
  163.   .no_window:
  164.  
  165. ;--------------------------
  166. ; Should a segment be sent?
  167.  
  168.         test    [eax + TCP_SOCKET.t_flags], TF_ACKNOW
  169.         jnz     .send
  170.  
  171.         test    dl, TH_SYN + TH_RST
  172.         jnz     .send
  173.  
  174.         mov     ebx, [eax + TCP_SOCKET.SND_UP]
  175.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  176.         jg      .send
  177.  
  178.         test    dl, TH_FIN
  179.         jz      .enter_persist
  180.  
  181.         test    [eax + TCP_SOCKET.t_flags], TF_SENTFIN
  182.         jnz     .send
  183.  
  184.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]
  185.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  186.         je      .send
  187.  
  188. ;--------------------
  189. ; Enter persist state
  190.  
  191.   .enter_persist:
  192.  
  193.         DEBUGF  1,"Entering persist state\n"
  194.  
  195. ;--------------------------------------
  196. ; No reason to send a segment, just ret
  197.  
  198.         DEBUGF  1,"No reason to send a segment\n"
  199.  
  200.         mov     [ebx + SOCKET.lock], 0
  201.  
  202.         ret
  203.  
  204.  
  205. ;-----------------------------------------------
  206. ;
  207. ; Send a segment
  208. ;
  209. ; eax = socket pointer
  210. ;  dl = flags
  211. ;
  212. ;-----------------------------------------------
  213.  
  214.   .send:
  215.  
  216.         DEBUGF  1,"Preparing to send a segment\n"
  217.  
  218.         mov     edi, TCP_segment.Data   ; edi will contain headersize
  219.  
  220.         sub     esp, 8                  ; create some space on stack
  221.         push    eax                     ; save this too..
  222.  
  223. ;------------------------------------
  224. ; Send options with first SYN segment
  225.  
  226.         test    dl, TH_SYN
  227.         jz      .no_options
  228.  
  229.         push    [eax + TCP_SOCKET.ISS]
  230.         pop     [eax + TCP_SOCKET.SND_NXT]
  231.  
  232.         test    [eax + TCP_SOCKET.t_flags], TF_NOOPT
  233.         jnz     .no_options
  234.  
  235.         mov     ecx, 1460
  236.         or      ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
  237.         bswap   ecx
  238.         push    ecx
  239.         add     di, 4
  240.  
  241.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
  242.         jz      .no_syn
  243.  
  244.         test    dl, TH_ACK
  245.         jnz     .scale_opt
  246.  
  247.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
  248.         jz      .no_syn
  249.  
  250.   .scale_opt:
  251.         movzx   ecx, byte [eax + TCP_SOCKET.request_r_scale]
  252.         or      ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
  253.         bswap   ecx
  254.         pushd   ecx
  255.         add     di, 4
  256.  
  257.   .no_syn:
  258.  
  259. ;------------------------------------
  260. ; Make the timestamp option if needed
  261.  
  262.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
  263.         jz      .no_timestamp
  264.  
  265.         test    dl, TH_RST
  266.         jnz     .no_timestamp
  267.  
  268.         test    dl, TH_ACK
  269.         jz      .timestamp
  270.  
  271.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
  272.         jz      .no_timestamp
  273.  
  274.   .timestamp:
  275.         mov     esi, [timer_ticks]
  276.         bswap   esi
  277.         push    esi
  278.         pushw   0
  279.         pushd   TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
  280.         add     di, 10
  281.  
  282.   .no_timestamp:
  283.         ;; TODO: check if we dont exceed the max segment size
  284.  
  285.   .no_options:
  286.         ; eax = socket ptr
  287.         ; edx = flags
  288.         ; ecx = data size
  289.         ; edi = header size
  290.         ; esi = snd ring buff ptr
  291.  
  292.         mov     ecx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
  293.         cmp     ecx, [eax + TCP_SOCKET.t_maxseg]                        ;;; right?
  294.         jle     @f
  295.         mov     ecx, [eax + TCP_SOCKET.t_maxseg]
  296.        @@:
  297.         add     ecx, edi        ; total TCP segment size
  298.  
  299. ; Start by pushing all TCP header values in reverse order on stack
  300. ; (essentially, creating the tcp header!)
  301.  
  302.         pushw   0       ;        .UrgentPointer          dw ?
  303.         pushw   0       ;        .Checksum               dw ?
  304.         pushw   0x00a0  ;        .Window                 dw ?    ;;;;;;;
  305.         shl     edi, 2  ;        .DataOffset             db ?  only 4 left-most bits
  306.         shl     dx, 8
  307.         or      dx, di  ;        .Flags                  db ?
  308.         pushw   dx
  309.         shr     edi, 2  ;        .DataOffset             db ? ;;;;
  310.  
  311.         push    [eax + TCP_SOCKET.RCV_NXT]      ;        .AckNumber              dd ?
  312.         ntohd   [esp]
  313.  
  314.         push    [eax + TCP_SOCKET.SND_NXT]      ;        .SequenceNumber         dd ?
  315.         ntohd   [esp]
  316.  
  317.         push    [eax + TCP_SOCKET.RemotePort]   ;        .DestinationPort        dw ?
  318.         ntohw   [esp]
  319.  
  320.         push    [eax + TCP_SOCKET.LocalPort]    ;        .SourcePort             dw ?
  321.         ntohw   [esp]
  322.  
  323.         push    edi             ; header size
  324.  
  325. ; Create the IP packet
  326.         mov     ebx, [eax + IP_SOCKET.LocalIP]  ; source ip
  327.         mov     eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
  328.         mov     di, IP_PROTO_TCP shl 8 + 128
  329.         call    IPv4_output
  330.         jz      .fail
  331.  
  332. ;-----------------------------------------
  333. ; Move TCP header from stack to TCP packet
  334.  
  335.         push    ecx
  336.         mov     ecx, [esp+4]
  337.         lea     esi, [esp+4+4]
  338.         shr     ecx, 2
  339.         rep     movsd
  340.         pop     ecx             ; full TCP packet size
  341.  
  342.         pop     esi             ; headersize
  343.         add     esp, esi
  344.  
  345.         mov     [esp + 4], eax          ; packet ptr
  346.         mov     [esp + 4+4], edx        ; packet size
  347.  
  348.         mov     edx, edi                ; begin of data
  349.         sub     edx, esi                ; begin of packet (edi = begin of data)
  350.         push    ecx
  351.         sub     ecx, esi                ; data size
  352.  
  353. ;--------------
  354. ; Copy the data
  355.  
  356. ; eax = ptr to ring struct
  357. ; ecx = buffer size
  358. ; edi = ptr to buffer
  359.  
  360. ;        test    ecx, ecx
  361.         mov     eax, [esp+4]              ; socket ptr
  362.         add     [eax + TCP_SOCKET.SND_NXT], ecx
  363.         add     eax, STREAM_SOCKET.snd
  364.         push    edx
  365.         call    SOCKET_ring_read
  366.         pop     esi
  367.         pop     ecx
  368.         pop     eax
  369.  
  370.         test    [esi + TCP_segment.Flags], TH_SYN + TH_FIN
  371.         jz      @f
  372.         inc     [eax + TCP_SOCKET.SND_NXT]
  373.         ;;; TODO: update sentfin flag
  374.        @@:
  375.  
  376.         mov     edx, [eax + TCP_SOCKET.SND_NXT]
  377.         cmp     edx, [eax + TCP_SOCKET.SND_MAX]
  378.         jle     @f
  379.         mov     [eax + TCP_SOCKET.SND_MAX], edx
  380.  
  381.         ;;;; TODO: time transmission (420)
  382.        @@:
  383.  
  384.         ;;; TODO: set retransmission timer
  385.  
  386. ;--------------------
  387. ; Create the checksum
  388.  
  389.         DEBUGF  1,"checksum: ptr=%x size=%u\n", esi, ecx
  390.  
  391.         TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
  392.         mov     [esi+TCP_segment.Checksum], dx
  393.  
  394. ;----------------
  395. ; Send the packet
  396.  
  397.         DEBUGF  1,"Sending TCP Packet to device %x\n", ebx
  398.         call    [ebx + NET_DEVICE.transmit]
  399.         ret
  400.  
  401.  
  402.   .fail:
  403.         pop     ecx
  404.         add     esp, ecx
  405.         add     esp, 4+8
  406.         DEBUGF 1,"TCP_output: failed\n"
  407.         ret
  408.  
  409.  
  410.