Subversion Repositories Kolibri OS

Rev

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