Subversion Repositories Kolibri OS

Rev

Rev 2305 | 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: 2362 $
  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.         mov     [eax + SOCKET.lock], 0
  268.  
  269.         ret
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279. ;-----------------------------------------------
  280. ;
  281. ; Send a segment (222)
  282. ;
  283. ; eax = socket pointer
  284. ; esi = data len
  285. ;  dl = flags
  286. ;
  287. ;-----------------------------------------------
  288.  
  289.   .send:
  290.  
  291.         DEBUGF  1,"Preparing to send a segment\n"
  292.  
  293.         mov     edi, sizeof.TCP_header  ; edi will contain headersize
  294.  
  295.         sub     esp, 8                  ; create some space on stack
  296.         push    eax                     ; save socket pointer
  297.  
  298. ;------------------------------------
  299. ; Send options with first SYN segment
  300.  
  301.         test    dl, TH_SYN
  302.         jz      .options_done
  303.  
  304.         push    [eax + TCP_SOCKET.ISS]
  305.         pop     [eax + TCP_SOCKET.SND_NXT]
  306.  
  307.         test    [eax + TCP_SOCKET.t_flags], TF_NOOPT
  308.         jnz     .options_done
  309.  
  310.         mov     ecx, 1460                              ;;;; FIXME
  311.         or      ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
  312.         bswap   ecx
  313.         push    ecx
  314.         add     di, 4
  315.  
  316.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
  317.         jz      .no_syn
  318.  
  319.         test    dl, TH_ACK
  320.         jnz     .scale_opt
  321.  
  322.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
  323.         jz      .no_syn
  324.  
  325.   .scale_opt:
  326.         movzx   ecx, byte [eax + TCP_SOCKET.request_r_scale]
  327.         or      ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
  328.         bswap   ecx
  329.         pushd   ecx
  330.         add     di, 4
  331.  
  332.   .no_syn:
  333.  
  334. ;------------------------------------
  335. ; Make the timestamp option if needed
  336.  
  337.         test    [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
  338.         jz      .no_timestamp
  339.  
  340.         test    dl, TH_RST
  341.         jnz     .no_timestamp
  342.  
  343.         test    dl, TH_ACK
  344.         jz      .timestamp
  345.  
  346.         test    [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
  347.         jz      .no_timestamp
  348.  
  349.   .timestamp:
  350.         mov     ebx, [timer_ticks]
  351.         bswap   ebx
  352.         push    ebx
  353.         pushw   0
  354.         pushd   TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
  355.         add     di, 10
  356.  
  357.   .no_timestamp:
  358.  
  359.         ; <Add additional options here>
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.   .options_done:
  369.  
  370. ; eax = socket ptr
  371. ; edx = flags
  372. ; edi = header size
  373. ; esi = data len
  374.  
  375. ;---------------------------------------------
  376. ; check if we dont exceed the max segment size (270)
  377.  
  378.         add     esi, edi                        ; total TCP segment size
  379.         cmp     esi, [eax + TCP_SOCKET.t_maxseg]
  380.         jbe     .no_overflow
  381.  
  382.         mov     esi, [eax + TCP_SOCKET.t_maxseg]
  383.  
  384. ;;; sendalot = 1
  385.  
  386.   .no_overflow:
  387.  
  388. ;-----------------------------------------------------------------
  389. ; Start by pushing all TCP header values in reverse order on stack
  390. ; (essentially, creating the tcp header on the stack!)
  391.  
  392.         pushw   0       ;        .UrgentPointer          dw ?
  393.         pushw   0       ;        .Checksum               dw ?
  394.         pushw   0x00a0  ;        .Window                 dw ?    ;;;;;;; FIXME
  395.         shl     edi, 2  ;        .DataOffset             db ?  only 4 left-most bits
  396.         shl     dx, 8
  397.         or      dx, di  ;        .Flags                  db ?
  398.         pushw   dx
  399.         shr     edi, 2  ;        .DataOffset             db ? ;;;;
  400.  
  401.         push    [eax + TCP_SOCKET.RCV_NXT]      ;        .AckNumber              dd ?
  402.         ntohd   [esp]
  403.  
  404.         push    [eax + TCP_SOCKET.SND_NXT]      ;        .SequenceNumber         dd ?
  405.         ntohd   [esp]
  406.  
  407.         push    [eax + TCP_SOCKET.RemotePort]   ;        .DestinationPort        dw ?
  408.         ntohw   [esp]
  409.  
  410.         push    [eax + TCP_SOCKET.LocalPort]    ;        .SourcePort             dw ?
  411.         ntohw   [esp]
  412.  
  413.         push    edi             ; header size
  414.  
  415. ;---------------------
  416. ; Create the IP packet
  417.  
  418.         mov     ecx, esi
  419.  
  420.         mov     ebx, [eax + IP_SOCKET.LocalIP]  ; source ip
  421.         mov     eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
  422.         mov     di, IP_PROTO_TCP shl 8 + 128
  423.         call    IPv4_output
  424.         jz      .fail
  425.  
  426. ;-----------------------------------------
  427. ; Move TCP header from stack to TCP packet
  428.  
  429.         push    ecx
  430.         mov     ecx, [esp+4]
  431.         lea     esi, [esp+4+4]
  432.         shr     ecx, 2
  433.         rep     movsd
  434.         pop     ecx             ; full TCP packet size
  435.  
  436.         pop     esi             ; headersize
  437.         add     esp, esi
  438.  
  439.         mov     [esp + 4], eax          ; packet ptr
  440.         mov     [esp + 4+4], edx        ; packet size
  441.  
  442.         mov     edx, edi                ; begin of data
  443.         sub     edx, esi                ; begin of packet (edi = begin of data)
  444.         push    ecx
  445.         sub     ecx, esi                ; data size
  446.  
  447. ;--------------
  448. ; Copy the data
  449.  
  450. ; eax = ptr to ring struct
  451. ; ecx = buffer size
  452. ; edi = ptr to buffer
  453.  
  454.         mov     eax, [esp+4]                    ; get socket ptr
  455.  
  456.         add     [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number
  457.  
  458.         add     eax, STREAM_SOCKET.snd
  459.         push    edx
  460.         call    SOCKET_ring_read
  461.         pop     esi                             ; begin of data
  462.         pop     ecx                             ; full packet size
  463.         pop     eax                             ; socket ptr
  464.  
  465. ;----------------------------------
  466. ; update sequence number and timers  (400)
  467.  
  468.         test    [esi + TCP_header.Flags], TH_SYN + TH_FIN
  469.         jz      @f
  470.         inc     [eax + TCP_SOCKET.SND_NXT]      ; syn and fin take a sequence number
  471.         test    [esi + TCP_header.Flags], TH_FIN
  472.         jz      @f
  473.         or      [eax + TCP_SOCKET.t_flags], TF_SENTFIN  ; if we sent a fin, set the sentfin flag
  474.        @@:
  475.  
  476.         mov     edx, [eax + TCP_SOCKET.SND_NXT]
  477.         cmp     edx, [eax + TCP_SOCKET.SND_MAX]
  478.         jbe     @f
  479.         mov     [eax + TCP_SOCKET.SND_MAX], edx
  480.  
  481.         ;;;; TODO: time transmission (420)
  482.  
  483.        @@:
  484.  
  485. ; set retransmission timer if not already set, and not doing an ACK or keepalive probe
  486.  
  487.         cmp     [eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; FIXME
  488.         jb      .retransmit_set
  489.  
  490.         cmp     edx, [eax + TCP_SOCKET.SND_UNA]         ; edx = [eax + TCP_SOCKET.SND_NXT]
  491.         je      .retransmit_set
  492.  
  493.         mov     edx, [eax + TCP_SOCKET.t_rxtcur]
  494.         mov     [eax + TCP_SOCKET.timer_retransmission], dx
  495.  
  496.         cmp     [eax + TCP_SOCKET.timer_persist], 0
  497.         jne     @f
  498.         mov     [eax + TCP_SOCKET.timer_persist], 0
  499.         mov     [eax + TCP_SOCKET.t_rxtshift], 0
  500.        @@:
  501.  
  502.   .retransmit_set:
  503.  
  504. ;--------------------
  505. ; Create the checksum
  506.  
  507.         DEBUGF  1,"checksum: ptr=%x size=%u\n", esi, ecx
  508.  
  509.         TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
  510.         mov     [esi + TCP_header.Checksum], dx
  511.  
  512. ; unlock socket
  513.  
  514.         mov     [eax + SOCKET.lock], 0
  515.  
  516. ;----------------
  517. ; Send the packet
  518.  
  519.         DEBUGF  1,"Sending TCP Packet to device %x\n", ebx
  520.         call    [ebx + NET_DEVICE.transmit]
  521.         ret
  522.  
  523.  
  524.   .fail:
  525.         pop     ecx
  526.         add     esp, ecx
  527.         pop     eax
  528.         add     esp, 8
  529.         mov     [eax + SOCKET.lock], 0
  530.         DEBUGF 1,"TCP_output: failed\n"
  531.         ret
  532.  
  533.  
  534.