Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                                 ;;
  3. ;; Copyright (C) KolibriOS team 2004-2012. All rights reserved.    ;;
  4. ;; Distributed under terms of the GNU General Public License       ;;
  5. ;;                                                                 ;;
  6. ;;  Part of the tcp/ip network stack for KolibriOS                 ;;
  7. ;;                                                                 ;;
  8. ;;   Written by hidnplayr@kolibrios.org                            ;;
  9. ;;                                                                 ;;
  10. ;;    Based on the code of 4.4BSD                                  ;;
  11. ;;                                                                 ;;
  12. ;;          GNU GENERAL PUBLIC LICENSE                             ;;
  13. ;;             Version 2, June 1991                                ;;
  14. ;;                                                                 ;;
  15. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  16.  
  17. $Revision: 2612 $
  18.  
  19. ;-----------------------------------------------------------------
  20. ;
  21. ; TCP_output
  22. ;
  23. ; IN:  eax = socket pointer
  24. ;
  25. ; OUT: /
  26. ;
  27. ;-----------------------------------------------------------------
  28. align 4
  29. TCP_output:
  30.  
  31.         DEBUGF 1,"TCP_output, socket: %x\n", eax
  32.  
  33.  
  34. ; We'll detect the length of the data to be transmitted, and flags to be used
  35. ; If there is some data, or any critical controls to send (SYN / RST), then transmit
  36. ; Otherwise, investigate further
  37.  
  38.         mov     ebx, [eax + TCP_SOCKET.SND_MAX]
  39.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  40.         jne     .not_idle
  41.  
  42.         mov     ebx, [eax + TCP_SOCKET.t_idle]
  43.         cmp     ebx, [eax + TCP_SOCKET.t_rxtcur]
  44.         jbe     .not_idle
  45.  
  46. ; We have been idle for a while and no ACKS are expected to clock out any data we send..
  47. ; Slow start to get ack "clock" running again.
  48.  
  49.         mov     ebx, [eax + TCP_SOCKET.t_maxseg]
  50.         mov     [eax + TCP_SOCKET.SND_CWND], ebx
  51.  
  52.   .not_idle:
  53.   .again:
  54.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]         ; calculate offset (71)
  55.         sub     ebx, [eax + TCP_SOCKET.SND_UNA]         ;
  56.  
  57.         mov     ecx, [eax + TCP_SOCKET.SND_WND]         ; determine window
  58.         cmp     ecx, [eax + TCP_SOCKET.SND_CWND]        ;
  59.         jb      @f                                      ;
  60.         mov     ecx, [eax + TCP_SOCKET.SND_CWND]        ;
  61.        @@:                                              ;
  62.  
  63.         call    TCP_outflags                            ; flags in dl
  64.  
  65. ;------------------------
  66. ; data being forced out ?
  67.  
  68. ; If in persist timeout with window of 0, send 1 byte.
  69. ; Otherwise, if window is small but nonzero, and timer expired,
  70. ; we will send what we can and go to transmit state
  71.  
  72.         test    [eax + TCP_SOCKET.t_force], -1
  73.         jz      .no_force
  74.  
  75.         test    ecx, ecx
  76.         jnz     .no_zero_window
  77.  
  78.         cmp     ebx, [eax + STREAM_SOCKET.snd.size]
  79.         jae     @f
  80.  
  81.         and     dl, not (TH_FIN)          ; clear the FIN flag    ??? how can it be set before?
  82.  
  83.        @@:
  84.         inc     ecx
  85.         jmp     .no_force
  86.  
  87.   .no_zero_window:
  88.         mov     [eax + TCP_SOCKET.timer_persist], 0
  89.         mov     [eax + TCP_SOCKET.t_rxtshift], 0
  90.  
  91.   .no_force:
  92.  
  93. ;--------------------------------
  94. ; Calculate how much data to send (106)
  95.  
  96.         mov     esi, [eax + STREAM_SOCKET.snd.size]
  97.         cmp     esi, ecx
  98.         jb      @f
  99.         mov     esi, ecx
  100.        @@:
  101.         sub     esi, ebx
  102.  
  103.  
  104. ;------------------------
  105. ; check for window shrink (107)
  106.  
  107. ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1
  108. ; Otherwise, window shrank after we sent into it.
  109.  
  110.         jae     .not_persist
  111.  
  112. ; enter persist state
  113.         xor     esi, esi
  114.  
  115. ; If window shrank to 0
  116.         test    ecx, ecx
  117.         jnz     @f
  118.  
  119. ; cancel pending retransmit
  120.         mov     [eax + TCP_SOCKET.timer_retransmission], 0
  121.  
  122. ; pull SND_NXT back to (closed) window, We will enter persist state below.
  123.         push    [eax + TCP_SOCKET.SND_UNA]
  124.         pop     [eax + TCP_SOCKET.SND_NXT]
  125.  
  126.        @@:
  127.  
  128. ; If window didn't close completely, just wait for an ACK
  129.  
  130.   .not_persist:
  131.  
  132. ;---------------------------
  133. ; Send one segment at a time (124)
  134.  
  135.         cmp     esi, [eax + TCP_SOCKET.t_maxseg]
  136.         jbe     @f
  137.  
  138.         mov     esi, [eax + TCP_SOCKET.t_maxseg]
  139.  
  140. ;;; sendalot = 1
  141.  
  142.        @@:
  143.  
  144. ;--------------------------------------------
  145. ; Turn of FIN flag if send buffer not emptied (128)
  146.  
  147.         mov     edi, [eax + TCP_SOCKET.SND_NXT]
  148.         add     edi, esi
  149.         sub     edi, [eax + TCP_SOCKET.SND_UNA]
  150.         sub     edi, [eax + STREAM_SOCKET.snd.size]
  151.         jns     @f
  152.  
  153.         and     dl, not (TH_FIN)
  154.  
  155.        @@:
  156.  
  157. ;-------------------------------
  158. ; calculate window advertisement (130)
  159.  
  160.         mov     ecx, SOCKET_MAXDATA
  161.         sub     ecx, [eax + STREAM_SOCKET.rcv.size]
  162.  
  163. ;------------------------------
  164. ; Sender silly window avoidance (131)
  165.  
  166.         test    esi, esi
  167.         jz      .len_zero
  168.  
  169.         cmp     esi, [eax + TCP_SOCKET.t_maxseg]
  170.         je      .send
  171.  
  172.         test    [eax + TCP_SOCKET.t_flags], TF_NODELAY
  173.         jnz     @f
  174.         ; TODO: if not 'idle', skip to next codeblock
  175.        @@:
  176.         add     ebx, esi
  177.         cmp     ebx, [eax + STREAM_SOCKET.snd.size]
  178.         jae     .send
  179.  
  180.         test    [eax + TCP_SOCKET.t_force], -1  ;;;
  181.         jnz     .send
  182.  
  183.         mov     ebx, [eax + TCP_SOCKET.max_sndwnd]
  184.         shr     ebx, 1
  185.         cmp     esi, ebx
  186.         jae     .send
  187.  
  188.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]
  189.         cmp     ebx, [eax + TCP_SOCKET.SND_MAX]
  190.         jb      .send
  191.  
  192.   .len_zero:
  193.  
  194. ;----------------------------------------
  195. ; Check if a window update should be sent (154)
  196.  
  197.         test    ecx, ecx
  198.         jz      .no_window
  199.  
  200.         push    ecx
  201.         mov     cl, [eax + TCP_SOCKET.RCV_SCALE]
  202.         inc     cl                                      ; we want it *2
  203.         mov     ebx, TCP_max_win
  204.         shl     ebx, cl
  205.         pop     ecx
  206.         cmp     ebx, ecx
  207.         cmovb   ebx, ecx
  208.  
  209.         ; now ebx is TWICE the amount we can increase the window
  210.         ; (with TCP_max_win shl rcv_scale as the maximum)
  211.  
  212.         cmp     ebx, [eax + TCP_SOCKET.t_maxseg]
  213.         jae     .send
  214.  
  215.   ;;;      cmp     ebx, [eax + ]                        ;;; TODO: check receive buffer high water mark
  216.     ;;;    jae     .send
  217.  
  218.   .no_window:
  219.  
  220. ;--------------------------
  221. ; Should a segment be sent? (174)
  222.  
  223.         test    [eax + TCP_SOCKET.t_flags], TF_ACKNOW   ; we need to ACK
  224.         jnz     .send
  225.  
  226.         test    dl, TH_SYN + TH_RST                     ; we need to send a SYN or RST
  227.         jnz     .send
  228.  
  229.         mov     ebx, [eax + TCP_SOCKET.SND_UP]          ; when urgent pointer is beyond start of send bufer
  230.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  231.         ja      .send
  232.  
  233.         test    dl, TH_FIN
  234.         jz      .enter_persist  ; no reason to send, enter persist state
  235.  
  236. ; FIN was set, only send if not already sent, or on retransmit
  237.  
  238.         test    [eax + TCP_SOCKET.t_flags], TF_SENTFIN
  239.         jnz     .send
  240.  
  241.         mov     ebx, [eax + TCP_SOCKET.SND_NXT]
  242.         cmp     ebx, [eax + TCP_SOCKET.SND_UNA]
  243.         je      .send
  244.  
  245. ;--------------------
  246. ; Enter persist state (191)
  247.  
  248.   .enter_persist:
  249.  
  250.         cmp     [eax + STREAM_SOCKET.snd.size], 0       ; Data ready to send?
  251.         jne     @f
  252.         cmp     [eax + TCP_SOCKET.timer_retransmission], 0
  253.         jne     @f
  254.         cmp     [eax + TCP_SOCKET.timer_persist], 0     ; Persist timer already expired?
  255.         jne     @f
  256.  
  257.         DEBUGF  1,"Entering persist state\n"
  258.  
  259.         mov     [eax + TCP_SOCKET.t_rxtshift], 0
  260.         TCP_set_persist eax
  261.        @@:
  262.  
  263. ;----------------------------
  264. ; No reason to send a segment (219)
  265.  
  266.         DEBUGF  1,"No reason to send a segment\n"
  267.  
  268.         pusha
  269.         lea     ecx, [eax + SOCKET.mutex]
  270.         call    mutex_unlock
  271.         popa
  272.  
  273.         ret
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283. ;-----------------------------------------------
  284. ;
  285. ; Send a segment (222)
  286. ;
  287. ; eax = socket pointer
  288. ; esi = data len
  289. ;  dl = flags
  290. ;
  291. ;-----------------------------------------------
  292.  
  293.   .send:
  294.  
  295.         DEBUGF  1,"Preparing to send a segment socket: %x length: %u flags: %x\n", eax, esi, dl
  296.  
  297.         mov     edi, sizeof.TCP_header  ; edi will contain headersize
  298.  
  299.         sub     esp, 8                  ; create some space on stack
  300.         push    eax                     ; save socket pointer
  301.  
  302. ;------------------------------------
  303. ; Send options with first SYN segment
  304.  
  305.         test    dl, TH_SYN
  306.         jz      .options_done
  307.  
  308.         push    [eax + TCP_SOCKET.ISS]
  309.         pop     [eax + TCP_SOCKET.SND_NXT]
  310.  
  311.         test    [eax + TCP_SOCKET.t_flags], TF_NOOPT
  312.         jnz     .options_done
  313.  
  314.         mov     ecx, 1460                              ;;;; FIXME
  315.         or      ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
  316.         bswap   ecx
  317.         push    ecx
  318.         add     di, 4
  319.  
  320.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
  321.         jz      .no_syn
  322.  
  323.         test    dl, TH_ACK
  324.         jnz     .scale_opt
  325.  
  326.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
  327.         jz      .no_syn
  328.  
  329.   .scale_opt:
  330.         movzx   ecx, byte [eax + TCP_SOCKET.request_r_scale]
  331.         or      ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
  332.         bswap   ecx
  333.         pushd   ecx
  334.         add     di, 4
  335.  
  336.   .no_syn:
  337.  
  338. ;------------------------------------
  339. ; Make the timestamp option if needed
  340.  
  341.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
  342.         jz      .no_timestamp
  343.  
  344.         test    dl, TH_RST
  345.         jnz     .no_timestamp
  346.  
  347.         test    dl, TH_ACK
  348.         jz      .timestamp
  349.  
  350.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
  351.         jz      .no_timestamp
  352.  
  353.   .timestamp:
  354.         mov     ebx, [timer_ticks]
  355.         bswap   ebx
  356.         push    ebx
  357.         pushw   0
  358.         pushd   TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
  359.         add     di, 10
  360.  
  361.   .no_timestamp:
  362.  
  363.         ; <Add additional options here>
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.  
  372.   .options_done:
  373.  
  374. ; eax = socket ptr
  375. ; edx = flags
  376. ; edi = header size
  377. ; esi = data len
  378.  
  379. ;---------------------------------------------
  380. ; check if we dont exceed the max segment size (270)
  381.  
  382.         add     esi, edi                        ; total TCP segment size
  383.         cmp     esi, [eax + TCP_SOCKET.t_maxseg]
  384.         jbe     .no_overflow
  385.  
  386.         mov     esi, [eax + TCP_SOCKET.t_maxseg]
  387.  
  388. ;;; sendalot = 1
  389.  
  390.   .no_overflow:
  391.  
  392. ;-----------------------------------------------------------------
  393. ; Start by pushing all TCP header values in reverse order on stack
  394. ; (essentially, creating the tcp header on the stack!)
  395.  
  396.         pushw   0       ;        .UrgentPointer          dw ?
  397.         pushw   0       ;        .Checksum               dw ?
  398.         pushw   0x00a0  ;        .Window                 dw ?    ;;;;;;; FIXME
  399.         shl     edi, 2  ;        .DataOffset             db ?  only 4 left-most bits
  400.         shl     dx, 8
  401.         or      dx, di  ;        .Flags                  db ?
  402.         pushw   dx
  403.         shr     edi, 2  ;        .DataOffset             db ? ;;;;
  404.  
  405.         push    [eax + TCP_SOCKET.RCV_NXT]      ;        .AckNumber              dd ?
  406.         ntohd   [esp]
  407.  
  408.         push    [eax + TCP_SOCKET.SND_NXT]      ;        .SequenceNumber         dd ?
  409.         ntohd   [esp]
  410.  
  411.         push    [eax + TCP_SOCKET.RemotePort]   ;        .DestinationPort        dw ?
  412.         ntohw   [esp]
  413.  
  414.         push    [eax + TCP_SOCKET.LocalPort]    ;        .SourcePort             dw ?
  415.         ntohw   [esp]
  416.  
  417.         push    edi             ; header size
  418.  
  419. ;---------------------
  420. ; Create the IP packet
  421.  
  422.         mov     ecx, esi
  423.  
  424.         mov     ebx, [eax + IP_SOCKET.LocalIP]  ; source ip
  425.         mov     eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
  426.         mov     di, IP_PROTO_TCP shl 8 + 128
  427.         call    IPv4_output
  428.         jz      .ip_error
  429.  
  430. ;-----------------------------------------
  431. ; Move TCP header from stack to TCP packet
  432.  
  433.         push    ecx
  434.         mov     ecx, [esp+4]
  435.         lea     esi, [esp+4+4]
  436.         shr     ecx, 2
  437.         rep     movsd
  438.         pop     ecx             ; full TCP packet size
  439.  
  440.         pop     esi             ; headersize
  441.         add     esp, esi
  442.  
  443.         mov     [esp + 4], eax          ; packet ptr
  444.         mov     [esp + 4+4], edx        ; packet size
  445.  
  446.         mov     edx, edi                ; begin of data
  447.         sub     edx, esi                ; begin of packet (edi = begin of data)
  448.         push    ecx
  449.         sub     ecx, esi                ; data size
  450.  
  451. ;--------------
  452. ; Copy the data
  453.  
  454. ; eax = ptr to ring struct
  455. ; ecx = buffer size
  456. ; edi = ptr to buffer
  457.  
  458.         mov     eax, [esp+4]                    ; get socket ptr
  459.  
  460.         add     [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number
  461.  
  462.         add     eax, STREAM_SOCKET.snd
  463.         push    edx
  464.         call    SOCKET_ring_read
  465.         pop     esi                             ; begin of data
  466.         pop     ecx                             ; full packet size
  467.         pop     eax                             ; socket ptr
  468.  
  469. ;----------------------------------
  470. ; update sequence number and timers  (400)
  471.  
  472.         test    [esi + TCP_header.Flags], TH_SYN + TH_FIN
  473.         jz      @f
  474.         inc     [eax + TCP_SOCKET.SND_NXT]      ; syn and fin take a sequence number
  475.         test    [esi + TCP_header.Flags], TH_FIN
  476.         jz      @f
  477.         or      [eax + TCP_SOCKET.t_flags], TF_SENTFIN  ; if we sent a fin, set the sentfin flag
  478.        @@:
  479.  
  480.         mov     edx, [eax + TCP_SOCKET.SND_NXT]
  481.         cmp     edx, [eax + TCP_SOCKET.SND_MAX]
  482.         jbe     @f
  483.         mov     [eax + TCP_SOCKET.SND_MAX], edx
  484.  
  485.         ;;;; TODO: time transmission (420)
  486.  
  487.        @@:
  488.  
  489. ; set retransmission timer if not already set, and not doing an ACK or keepalive probe
  490.  
  491.         cmp     [eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; FIXME
  492.         jb      .retransmit_set
  493.  
  494.         cmp     edx, [eax + TCP_SOCKET.SND_UNA]         ; edx = [eax + TCP_SOCKET.SND_NXT]
  495.         je      .retransmit_set
  496.  
  497.         mov     edx, [eax + TCP_SOCKET.t_rxtcur]
  498.         mov     [eax + TCP_SOCKET.timer_retransmission], dx
  499.  
  500.         cmp     [eax + TCP_SOCKET.timer_persist], 0
  501.         jne     @f
  502.         mov     [eax + TCP_SOCKET.timer_persist], 0
  503.         mov     [eax + TCP_SOCKET.t_rxtshift], 0
  504.        @@:
  505.  
  506.   .retransmit_set:
  507.  
  508. ;--------------------
  509. ; Create the checksum
  510.  
  511.         DEBUGF  1,"checksum: ptr=%x size=%u\n", esi, ecx
  512.  
  513.         TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
  514.         mov     [esi + TCP_header.Checksum], dx
  515.  
  516. ; unlock socket
  517.  
  518.         pusha
  519.         lea     ecx, [eax + SOCKET.mutex]
  520.         call    mutex_unlock
  521.         popa
  522.  
  523. ;----------------
  524. ; Send the packet
  525.  
  526.         DEBUGF  1,"Sending TCP Packet to device %x\n", ebx
  527.         call    [ebx + NET_DEVICE.transmit]
  528.         ret
  529.  
  530.  
  531.   .ip_error:
  532.         pop     ecx
  533.         add     esp, ecx
  534.         pop     eax
  535.         add     esp, 8
  536.  
  537.         pusha
  538.         lea     ecx, [eax + SOCKET.mutex]
  539.         call    mutex_unlock
  540.         popa
  541.  
  542.         DEBUGF 1,"TCP_output: IP error\n"
  543.         ret
  544.  
  545.  
  546.