Subversion Repositories Kolibri OS

Rev

Rev 2995 | 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. ;;  IPv4.INC                                                       ;;
  7. ;;                                                                 ;;
  8. ;;  Part of the TCP/IP network stack for KolibriOS                 ;;
  9. ;;                                                                 ;;
  10. ;;  Based on the work of [Johnny_B] and [smb]                      ;;
  11. ;;                                                                 ;;
  12. ;;    Written by hidnplayr@kolibrios.org                           ;;
  13. ;;                                                                 ;;
  14. ;;          GNU GENERAL PUBLIC LICENSE                             ;;
  15. ;;             Version 2, June 1991                                ;;
  16. ;;                                                                 ;;
  17. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  18.  
  19. $Revision: 3143 $
  20.  
  21. MAX_FRAGMENTS                   = 64
  22.  
  23. struct  IPv4_header
  24.  
  25.         VersionAndIHL           db ?    ; Version[0-3 bits] and IHL(header length)[4-7 bits]
  26.         TypeOfService           db ?    ; precedence [7-5] minimize delay [4], maximize throughput [3], maximize riliability [2] minimize momentary cost [1] and zero [0]
  27.         TotalLength             dw ?
  28.         Identification          dw ?
  29.         FlagsAndFragmentOffset  dw ?    ; Flags[0-2] and FragmentOffset[3-15]
  30.         TimeToLive              db ?    ;
  31.         Protocol                db ?
  32.         HeaderChecksum          dw ?
  33.         SourceAddress           dd ?
  34.         DestinationAddress      dd ?
  35.  
  36. ends
  37.  
  38. struct  FRAGMENT_slot
  39.  
  40.         ttl                     dw ?    ; Time to live for this entry, 0 for empty slot's
  41.         id                      dw ?    ; Identification field from IP header
  42.         SrcIP                   dd ?    ; .. from IP header
  43.         DstIP                   dd ?    ; .. from IP header
  44.         ptr                     dd ?    ; Pointer to first packet
  45.  
  46. ends
  47.  
  48. struct  FRAGMENT_entry                  ; This structure will replace the ethernet header in fragmented ip packets
  49.  
  50.         PrevPtr                 dd ?    ; Pointer to previous fragment entry  (-1 for first packet)
  51.         NextPtr                 dd ?    ; Pointer to next fragment entry (-1 for last packet)
  52.         Owner                   dd ?    ; Pointer to structure of driver
  53.                                 rb 2    ; to match ethernet header size         ;;; FIXME
  54.                                         ; Ip header begins here (we will need the IP header to re-construct the complete packet)
  55. ends
  56.  
  57.  
  58. align 4
  59. uglobal
  60.  
  61.         IP_LIST         rd MAX_NET_DEVICES
  62.         SUBNET_LIST     rd MAX_NET_DEVICES
  63.         DNS_LIST        rd MAX_NET_DEVICES
  64.         GATEWAY_LIST    rd MAX_NET_DEVICES
  65.         BROADCAST_LIST  rd MAX_NET_DEVICES
  66.  
  67.         IP_PACKETS_TX   rd MAX_NET_DEVICES
  68.         IP_PACKETS_RX   rd MAX_NET_DEVICES
  69.  
  70.         FRAGMENT_LIST   rb MAX_FRAGMENTS * sizeof.FRAGMENT_slot
  71. endg
  72.  
  73.  
  74. ;-----------------------------------------------------------------
  75. ;
  76. ; IPv4_init
  77. ;
  78. ;  This function resets all IP variables
  79. ;
  80. ;-----------------------------------------------------------------
  81. macro   IPv4_init {
  82.  
  83.         xor     eax, eax
  84.         mov     edi, IP_LIST
  85.         mov     ecx, 7*MAX_NET_DEVICES + (sizeof.FRAGMENT_slot*MAX_FRAGMENTS)/4
  86.         rep     stosd
  87.  
  88. }
  89.  
  90.  
  91. ;-----------------------------------------------------------------
  92. ;
  93. ; Decrease TimeToLive of all fragment slots
  94. ;
  95. ;-----------------------------------------------------------------
  96. macro IPv4_decrease_fragment_ttls {
  97.  
  98. local   .loop, .next
  99.  
  100.         mov     esi, FRAGMENT_LIST
  101.         mov     ecx, MAX_FRAGMENTS
  102.   .loop:
  103.         cmp     [esi + FRAGMENT_slot.ttl], 0
  104.         je      .next
  105.         dec     [esi + FRAGMENT_slot.ttl]
  106.         jz      .died
  107.   .next:
  108.         add     esi, sizeof.FRAGMENT_slot
  109.         dec     ecx
  110.         jnz     .loop
  111.         jmp     .done
  112.  
  113.   .died:
  114.         DEBUGF 1,"Fragment slot timed-out!\n"
  115. ;;; TODO: clear all entry's of timed-out slot
  116.         jmp     .next
  117.  
  118.   .done:
  119. }
  120.  
  121.  
  122.  
  123. macro IPv4_checksum ptr {
  124.  
  125. ; This is the fast procedure to create or check an IP header without options
  126. ; To create a new checksum, the checksum field must be set to 0 before computation
  127. ; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct
  128.  
  129.         push    ebx
  130.         xor     ebx, ebx
  131.         add     bl, [ptr+1]
  132.         adc     bh, [ptr+0]
  133.  
  134.         adc     bl, [ptr+3]
  135.         adc     bh, [ptr+2]
  136.  
  137.         adc     bl, [ptr+5]
  138.         adc     bh, [ptr+4]
  139.  
  140.         adc     bl, [ptr+7]
  141.         adc     bh, [ptr+6]
  142.  
  143.         adc     bl, [ptr+9]
  144.         adc     bh, [ptr+8]
  145.  
  146. ; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation
  147.  
  148.         adc     bl, [ptr+13]
  149.         adc     bh, [ptr+12]
  150.  
  151.         adc     bl, [ptr+15]
  152.         adc     bh, [ptr+14]
  153.  
  154.         adc     bl, [ptr+17]
  155.         adc     bh, [ptr+16]
  156.  
  157.         adc     bl, [ptr+19]
  158.         adc     bh, [ptr+18]
  159.  
  160.         adc     ebx, 0
  161.  
  162.         push    ecx
  163.         mov     ecx, ebx
  164.         shr     ecx, 16
  165.         and     ebx, 0xffff
  166.         add     ebx, ecx
  167.  
  168.         mov     ecx, ebx
  169.         shr     ecx, 16
  170.         add     ebx, ecx
  171.  
  172.         not     bx
  173.         jnz     .not_zero
  174.         dec     bx
  175.   .not_zero:
  176.         xchg    bl, bh
  177.         pop     ecx
  178.  
  179.         neg     word [ptr+10]           ; zero will stay zero so we just get the checksum
  180.         add     word [ptr+10], bx       ;  , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
  181.         pop     ebx
  182.  
  183. }
  184.  
  185.  
  186.  
  187. ;-----------------------------------------------------------------
  188. ;
  189. ; IPv4_input:
  190. ;
  191. ;  Will check if IPv4 Packet isnt damaged
  192. ;  and call appropriate handler. (TCP/UDP/ICMP/..)
  193. ;
  194. ;  It will also re-construct fragmented packets
  195. ;
  196. ;  IN:  Pointer to buffer in [esp]
  197. ;       size of buffer in [esp+4]
  198. ;       pointer to device struct in ebx
  199. ;       pointer to IPv4 header in edx
  200. ;       size of IPv4 packet in ecx
  201. ;  OUT: /
  202. ;
  203. ;-----------------------------------------------------------------
  204. align 4
  205. IPv4_input:                                                     ; TODO: add IPv4 raw sockets support
  206.  
  207.         DEBUGF  1,"IPv4_input, packet from: %u.%u.%u.%u ",\
  208.         [edx + IPv4_header.SourceAddress + 0]:1,[edx + IPv4_header.SourceAddress + 1]:1,\
  209.         [edx + IPv4_header.SourceAddress + 2]:1,[edx + IPv4_header.SourceAddress + 3]:1
  210.         DEBUGF  1,"to: %u.%u.%u.%u\n",\
  211.         [edx + IPv4_header.DestinationAddress + 0]:1,[edx + IPv4_header.DestinationAddress + 1]:1,\
  212.         [edx + IPv4_header.DestinationAddress + 2]:1,[edx + IPv4_header.DestinationAddress + 3]:1
  213.  
  214. ;-------------------------------
  215. ; re-calculate the checksum
  216.  
  217.         IPv4_checksum edx
  218.         jnz     .dump                                           ; if checksum isn't valid then dump packet
  219.  
  220.         DEBUGF  1,"IPv4_input: Checksum ok\n"
  221.  
  222. ;-----------------------------------
  223. ; Check if destination IP is correct
  224.  
  225.         call    NET_ptr_to_num
  226.         shl     edi, 2
  227.  
  228.         ; check if it matches local ip
  229.  
  230.         mov     eax, [edx + IPv4_header.DestinationAddress]
  231.         cmp     eax, [IP_LIST+edi]
  232.         je      .ip_ok
  233.  
  234.         ; check for broadcast (IP or (not SUBNET))
  235.  
  236.         cmp     eax, [BROADCAST_LIST+edi]
  237.         je      .ip_ok
  238.  
  239.         ; or a special broadcast (255.255.255.255)
  240.  
  241.         cmp     eax, 0xffffffff
  242.         je      .ip_ok
  243.  
  244.         ; maybe it's a multicast (224.0.0.0/4)
  245.  
  246.         and     eax, 0x0fffffff
  247.         cmp     eax, 224
  248.         je      .ip_ok
  249.  
  250.         ; or a loopback address (127.0.0.0/8)
  251.  
  252.         and     eax, 0x00ffffff
  253.         cmp     eax, 127
  254.         je      .ip_ok
  255.  
  256.         ; or it's just not meant for us.. :(
  257.  
  258.         DEBUGF  2,"IPv4_input: Destination address does not match!\n"
  259.         jmp     .dump
  260.  
  261. ;------------------------
  262. ; Now we can update stats
  263.  
  264.   .ip_ok:
  265.         inc     [IP_PACKETS_RX+edi]
  266.  
  267. ;----------------------------------
  268. ; Check if the packet is fragmented
  269.  
  270.         test    [edx + IPv4_header.FlagsAndFragmentOffset], 1 shl 5     ; Is 'more fragments' flag set ?
  271.         jnz     .has_fragments                                          ; If so, we definately have a fragmented packet
  272.  
  273.         test    [edx + IPv4_header.FlagsAndFragmentOffset], 0xff1f      ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
  274.         jnz     .is_last_fragment
  275.  
  276. ;-------------------------------------------------------------------
  277. ; No, it's just a regular IP packet, pass it to the higher protocols
  278.  
  279.   .handle_it:                                                   ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
  280.  
  281.         movzx   esi, [edx + IPv4_header.VersionAndIHL]          ; Calculate Header length by using IHL field
  282.         and     esi, 0x0000000f                                 ;
  283.         shl     esi, 2                                          ;
  284.  
  285.         movzx   ecx, [edx + IPv4_header.TotalLength]            ; Calculate length of encapsulated Packet
  286.         xchg    cl, ch                                          ;
  287.         sub     ecx, esi                                        ;
  288.  
  289.         lea     edi, [edx + IPv4_header.SourceAddress]          ; make edi ptr to source and dest IPv4 address
  290.         mov     al, [edx + IPv4_header.Protocol]
  291.         add     esi, edx                                        ; make esi ptr to data
  292.  
  293.         cmp     al, IP_PROTO_TCP
  294.         je      TCP_input
  295.  
  296.         cmp     al, IP_PROTO_UDP
  297.         je      UDP_input
  298.  
  299.         cmp     al, IP_PROTO_ICMP
  300.         je      ICMP_input
  301.  
  302.         DEBUGF  2,"IPv4_input: unknown protocol %u\n", al
  303.  
  304.   .dump:
  305.         DEBUGF  2,"IPv4_input: dumping\n"
  306. ;        inc     [dumped_rx_count]                              ;;; TODO
  307.         call    kernel_free
  308.         add     esp, 4                                          ; pop (balance stack)
  309.         ret
  310.  
  311.  
  312. ;---------------------------
  313. ; Fragmented packet handler
  314.  
  315.  
  316.   .has_fragments:
  317.         movzx   eax, [edx + IPv4_header.FlagsAndFragmentOffset]
  318.         xchg    al, ah
  319.         shl     ax, 3
  320.  
  321.         DEBUGF  1,"IPv4_input: fragmented packet offset=%u id=%x\n", ax, [edx + IPv4_header.Identification]:4
  322.  
  323.         test    ax, ax                                          ; Is this the first packet of the fragment?
  324.         jz      .is_first_fragment
  325.  
  326.  
  327. ;-------------------------------------------------------
  328. ; We have a fragmented IP packet, but it's not the first
  329.  
  330.         DEBUGF  1,"IPv4_input: Middle fragment packet received!\n"
  331.  
  332.         call    IPv4_find_fragment_slot
  333.         cmp     esi, -1
  334.         je      .dump
  335.  
  336.         mov     [esi + FRAGMENT_slot.ttl], 15                   ; Reset the ttl
  337.         mov     esi, [esi + FRAGMENT_slot.ptr]
  338.         or      edi, -1
  339.   .find_last_entry:                                             ; The following routine will try to find the last entry
  340.         cmp     edi, [esi + FRAGMENT_entry.PrevPtr]
  341.         jne     .destroy_slot                                   ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
  342.         mov     edi, esi
  343.         mov     esi, [esi + FRAGMENT_entry.NextPtr]
  344.         cmp     esi, -1
  345.         jne     .find_last_entry
  346.                                                                 ; We found the last entry (pointer is now in edi)
  347.                                                                 ; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure
  348.  
  349.         pop     eax                                             ; pointer to packet
  350.         mov     [edi + FRAGMENT_entry.NextPtr], eax             ; update pointer of previous entry to the new entry
  351.         mov     [eax + FRAGMENT_entry.NextPtr], -1
  352.         mov     [eax + FRAGMENT_entry.PrevPtr], edi
  353.         mov     [eax + FRAGMENT_entry.Owner], ebx
  354.  
  355.         add     esp, 4
  356.         ret
  357.  
  358.  
  359. ;------------------------------------
  360. ; We have received the first fragment
  361.  
  362.   .is_first_fragment:
  363.         DEBUGF  1,"IPv4_input: First fragment packet received!\n"
  364.                                                                 ; try to locate a free slot..
  365.         mov     ecx, MAX_FRAGMENTS
  366.         mov     esi, FRAGMENT_LIST
  367.   .find_free_slot:
  368.         cmp     word [esi + FRAGMENT_slot.ttl], 0
  369.         je      .found_free_slot
  370.         add     esi, sizeof.FRAGMENT_slot
  371.         loop    .find_free_slot
  372.         jmp     .dump                                           ; If no free slot was found, dump the packet
  373.  
  374.   .found_free_slot:                                             ; We found a free slot, let's fill in the FRAGMENT_slot structure
  375.         mov     [esi + FRAGMENT_slot.ttl], 15                   ; RFC recommends 15 secs as ttl
  376.         mov     ax, [edx + IPv4_header.Identification]
  377.         mov     [esi + FRAGMENT_slot.id], ax
  378.         mov     eax, [edx + IPv4_header.SourceAddress]
  379.         mov     [esi + FRAGMENT_slot.SrcIP], eax
  380.         mov     eax, [edx + IPv4_header.DestinationAddress]
  381.         mov     [esi + FRAGMENT_slot.DstIP], eax
  382.         pop     eax
  383.         mov     [esi + FRAGMENT_slot.ptr], eax
  384.                                                                 ; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
  385.         mov     [eax + FRAGMENT_entry.NextPtr], -1
  386.         mov     [eax + FRAGMENT_entry.PrevPtr], -1
  387.         mov     [eax + FRAGMENT_entry.Owner], ebx
  388.  
  389.         add     esp, 4                                          ; balance stack and exit
  390.         ret
  391.  
  392.  
  393. ;-----------------------------------
  394. ; We have received the last fragment
  395.  
  396.   .is_last_fragment:
  397.         DEBUGF  1,"IPv4_input: Last fragment packet received!\n"
  398.  
  399.         call    IPv4_find_fragment_slot
  400.         cmp     esi, -1
  401.         je      .dump
  402.  
  403.         mov     esi, [esi + FRAGMENT_slot.ptr]                  ; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer
  404.         push    esi
  405.         xor     eax, eax
  406.         or      edi, -1
  407.  
  408.   .count_bytes:
  409.         cmp     [esi + FRAGMENT_entry.PrevPtr], edi
  410.         jne     .destroy_slot_pop                                               ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
  411.         mov     cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.TotalLength]       ; Add total length
  412.         xchg    cl, ch
  413.         DEBUGF  1,"IPv4_input: Packet size=%u\n", cx
  414.         add     ax, cx
  415.         movzx   cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL]     ; Sub Header length
  416.         and     cx, 0x000F
  417.         shl     cx, 2
  418.         DEBUGF  1,"IPv4_input: Header size=%u\n", cx
  419.         sub     ax, cx
  420.         mov     edi, esi
  421.         mov     esi, [esi + FRAGMENT_entry.NextPtr]
  422.         cmp     esi, -1
  423.         jne     .count_bytes
  424.  
  425.         mov     esi, [esp+4]
  426.         mov     [edi + FRAGMENT_entry.NextPtr], esi                            ; Add this packet to the chain, this simplifies the following code
  427.         mov     [esi + FRAGMENT_entry.NextPtr], -1
  428.         mov     [esi + FRAGMENT_entry.PrevPtr], edi
  429.         mov     [esi + FRAGMENT_entry.Owner], ebx
  430.  
  431.         mov     cx, [edx + IPv4_header.TotalLength]                            ; Note: This time we dont substract Header length
  432.         xchg    cl, ch
  433.         DEBUGF  1,"IPv4_input: Packet size=%u\n", cx
  434.         add     ax, cx
  435.         DEBUGF  1,"IPv4_input: Total Received data size=%u\n", eax
  436.  
  437.         push    eax
  438.         mov     ax, [edx + IPv4_header.FlagsAndFragmentOffset]
  439.         xchg    al, ah
  440.         shl     ax, 3
  441.         add     cx, ax
  442.         pop     eax
  443.         DEBUGF  1,"IPv4_input: Total Fragment size=%u\n", ecx
  444.  
  445.         cmp     ax, cx
  446.         jne     .destroy_slot_pop
  447.  
  448.         push    eax
  449.         push    eax
  450.         call    kernel_alloc
  451.         test    eax, eax
  452.         je      .destroy_slot_pop                                                       ; If we dont have enough space to allocate the buffer, discard all packets in slot
  453.         mov     edx, [esp+4]                                                            ; Get pointer to first fragment entry back in edx
  454.  
  455.   .rebuild_packet_loop:
  456.         movzx   ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.FlagsAndFragmentOffset] ; Calculate the fragment offset
  457.         xchg    cl, ch                                                                  ;  intel byte order
  458.         shl     cx, 3                                                                   ;   multiply by 8 and clear first 3 bits
  459.         DEBUGF  1,"IPv4_input: Fragment offset=%u\n", cx
  460.  
  461.         lea     edi, [eax + ecx]                                                        ; Notice that edi will be equal to eax for first fragment
  462.         movzx   ebx, [edx + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL]          ; Find header size (in ebx) of fragment
  463.         and     bx, 0x000F                                                              ;
  464.         shl     bx, 2                                                                   ;
  465.  
  466.         lea     esi, [edx + sizeof.FRAGMENT_entry]                                      ; Set esi to the correct begin of fragment
  467.         movzx   ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.TotalLength]            ; Calculate total length of fragment
  468.         xchg    cl, ch                                                                  ;  intel byte order
  469.  
  470.         cmp     edi, eax                                                                ; Is this packet the first fragment ?
  471.         je      .first_fragment
  472.         sub     cx, bx                                                                  ; If not, dont copy the header
  473.         add     esi, ebx                                                                ;
  474.   .first_fragment:
  475.  
  476.         push    cx                                                                      ; First copy dword-wise, then byte-wise
  477.         shr     cx, 2                                                                   ;
  478.         rep     movsd                                                                   ;
  479.         pop     cx                                                                      ;
  480.         and     cx, 3                                                                   ;
  481.         rep     movsb                                                                   ;
  482.  
  483.         push    eax
  484.         push    edx                                                                     ; Push pointer to fragment onto stack
  485.         mov     ebx, [edx + FRAGMENT_entry.Owner]                                       ; we need to remeber the owner, in case this is the last packet
  486.         mov     edx, [edx + FRAGMENT_entry.NextPtr]                                     ; Set edx to the next pointer
  487.         call    kernel_free                                                             ; free the previous fragment buffer (this uses the value from stack)
  488.         pop     eax
  489.         cmp     edx, -1                                                                 ; Check if it is last fragment in chain
  490.         jne     .rebuild_packet_loop
  491.  
  492.         pop     ecx
  493.         xchg    cl, ch
  494.         mov     edx, eax
  495.         mov     [edx + IPv4_header.TotalLength], cx
  496.         add     esp, 8
  497.         xchg    cl, ch
  498.         push    ecx
  499.  
  500.         push    eax
  501.         jmp     .handle_it          ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr
  502.  
  503.   .destroy_slot_pop:
  504.         add     esp, 4
  505.   .destroy_slot:
  506.         DEBUGF  1,"IPv4_input: Destroy fragment slot!\n"
  507.         ; TODO!
  508.         jmp     .dump
  509.  
  510.  
  511.  
  512.  
  513.  
  514. ;-----------------------------------------------------------------
  515. ;
  516. ; find fragment slot
  517. ;
  518. ; IN: pointer to fragmented packet in edx
  519. ; OUT: pointer to slot in esi, -1 on error
  520. ;
  521. ;-----------------------------------------------------------------
  522. align 4
  523. IPv4_find_fragment_slot:
  524.  
  525. ;;; TODO: the RFC says we should check protocol number too
  526.  
  527.         push    eax ebx ecx edx
  528.         mov     ax, [edx + IPv4_header.Identification]
  529.         mov     ecx, MAX_FRAGMENTS
  530.         mov     esi, FRAGMENT_LIST
  531.         mov     ebx, [edx + IPv4_header.SourceAddress]
  532.         mov     edx, [edx + IPv4_header.DestinationAddress]
  533.   .find_slot:
  534.         cmp     [esi + FRAGMENT_slot.id], ax
  535.         jne     .try_next
  536.         cmp     [esi + FRAGMENT_slot.SrcIP], ebx
  537.         jne     .try_next
  538.         cmp     [esi + FRAGMENT_slot.DstIP], edx
  539.         je      .found_slot
  540.   .try_next:
  541.         add     esi, sizeof.FRAGMENT_slot
  542.         loop    .find_slot
  543.  
  544.         or      esi, -1
  545.   .found_slot:
  546.         pop     edx ecx ebx eax
  547.         ret
  548.  
  549.  
  550. ;------------------------------------------------------------------
  551. ;
  552. ; IPv4_output
  553. ;
  554. ; IN: eax = dest ip
  555. ;     ebx = output device ptr/0 for automatic choice
  556. ;     ecx = data length
  557. ;     edx = source ip
  558. ;     di  = TTL shl 8 + protocol
  559. ;
  560. ; OUT: eax = pointer to buffer start
  561. ;      ebx = pointer to device struct (needed for sending procedure)
  562. ;      ecx = unchanged (packet size of embedded data)
  563. ;      edx = size of complete buffer
  564. ;      edi = pointer to start of data (0 on error)
  565. ;
  566. ;------------------------------------------------------------------
  567. align 4
  568. IPv4_output:
  569.  
  570.         DEBUGF  1,"IPv4_output: size=%u\n", ecx
  571.  
  572.         cmp     ecx, 65500              ; Max IPv4 packet size
  573.         ja      .too_large
  574.  
  575.         push    ecx eax edx di
  576.  
  577.         cmp     eax, 1 shl 24 + 127
  578.         je      .loopback
  579.  
  580.         call    IPv4_dest_to_dev        ; outputs device number in edi, dest ip in eax
  581.         call    ARP_IP_to_MAC
  582.         test    eax, 0xffff0000         ; error bits
  583.         jnz     .arp_error
  584.         push    ebx                     ; push the mac onto the stack
  585.         push    ax
  586.  
  587.         inc     [IP_PACKETS_TX + edi]   ; update stats
  588.  
  589.         mov     ebx, [NET_DRV_LIST + edi]
  590.         lea     eax, [ebx + ETH_DEVICE.mac]
  591.         mov     edx, esp
  592.         mov     ecx, [esp + 10 + 6]
  593.         add     ecx, sizeof.IPv4_header
  594.         mov     di, ETHER_IPv4
  595.         call    ETH_output
  596.         jz      .eth_error
  597.         add     esp, 6                  ; pop the mac out of the stack
  598.  
  599.   .continue:
  600.         xchg    cl, ch                                  ; internet byte order
  601.         mov     [edi + IPv4_header.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
  602.         mov     [edi + IPv4_header.TypeOfService], 0    ; nothing special, just plain ip packet
  603.         mov     [edi + IPv4_header.TotalLength], cx
  604.         mov     [edi + IPv4_header.Identification], 0   ; fragment id: FIXME
  605.         mov     [edi + IPv4_header.FlagsAndFragmentOffset], 0
  606.         pop     word [edi + IPv4_header.TimeToLive]     ; ttl shl 8 + protocol
  607. ;               [edi + IPv4_header.Protocol]
  608.         mov     [edi + IPv4_header.HeaderChecksum], 0
  609.         popd    [edi + IPv4_header.SourceAddress]
  610.         popd    [edi + IPv4_header.DestinationAddress]
  611.  
  612.         pop     ecx
  613.  
  614.         IPv4_checksum edi
  615.         add     edi, sizeof.IPv4_header
  616.         DEBUGF  1,"IPv4_output: success!\n"
  617.         ret
  618.  
  619.   .eth_error:
  620.         DEBUGF  1,"IPv4_output: ethernet error\n"
  621.         add     esp, 3*4+2+6
  622.         xor     edi, edi
  623.         ret
  624.  
  625.   .arp_error:
  626.         DEBUGF  1,"IPv4_output: ARP error=%x\n", eax
  627.         add     esp, 3*4+2
  628.         xor     edi, edi
  629.         ret
  630.  
  631.   .too_large:
  632.         DEBUGF  1,"IPv4_output: Packet too large!\n"
  633.         xor     edi, edi
  634.         ret
  635.  
  636.   .loopback:
  637.         mov     dword [esp + 2], eax
  638.         add     ecx, sizeof.IPv4_header
  639.         mov     di, ETHER_IPv4
  640.         call    LOOP_output
  641.         jmp     .continue
  642.  
  643.  
  644.  
  645.  
  646. ;------------------------------------------------------------------
  647. ;
  648. ; IPv4_output_raw
  649. ;
  650. ; IN: eax = socket ptr
  651. ;     ecx = data length
  652. ;     esi = data ptr
  653. ;
  654. ; OUT: /
  655. ;
  656. ;------------------------------------------------------------------
  657. align 4
  658. IPv4_output_raw:
  659.  
  660.         DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax
  661.  
  662.         cmp     ecx, 1480               ;;;;; FIXME
  663.         ja      .too_large
  664.  
  665.         sub     esp, 8
  666.         push    esi eax
  667.  
  668.         call    IPv4_dest_to_dev
  669.         call    ARP_IP_to_MAC
  670.  
  671.         test    eax, 0xffff0000         ; error bits
  672.         jnz     .arp_error
  673.  
  674.         push    ebx                     ; push the mac
  675.         push    ax
  676.  
  677.         inc     [IP_PACKETS_TX + edi]
  678.         mov     ebx, [NET_DRV_LIST + edi]
  679.         lea     eax, [ebx + ETH_DEVICE.mac]
  680.         mov     edx, esp
  681.         mov     ecx, [esp + 6 + 4]
  682.         add     ecx, sizeof.IPv4_header
  683.         mov     di, ETHER_IPv4
  684.         call    ETH_output
  685.         jz      .error
  686.  
  687.         add     esp, 6  ; pop the mac
  688.  
  689.         mov     dword[esp+4+4], edx
  690.         mov     dword[esp+4+4+4], eax
  691.  
  692.         pop     eax esi
  693. ;; todo: check socket options if we should add header, or just compute checksum
  694.  
  695.         push    edi ecx
  696.         rep     movsb
  697.         pop     ecx edi
  698.  
  699. ;        [edi + IPv4_header.VersionAndIHL]              ; IPv4, normal length (no Optional header)
  700. ;        [edi + IPv4_header.TypeOfService]              ; nothing special, just plain ip packet
  701. ;        [edi + IPv4_header.TotalLength]
  702. ;        [edi + IPv4_header.TotalLength]                ; internet byte order
  703. ;        [edi + IPv4_header.FlagsAndFragmentOffset]
  704.  
  705.         mov     [edi + IPv4_header.HeaderChecksum], 0
  706.  
  707. ;        [edi + IPv4_header.TimeToLive]                 ; ttl shl 8 + protocol
  708. ;        [edi + IPv4_header.Protocol]
  709. ;        [edi + IPv4_header.Identification]             ; fragment id
  710. ;        [edi + IPv4_header.SourceAddress]
  711. ;        [edi + IPv4_header.DestinationAddress]
  712.  
  713.         IPv4_checksum edi                       ;;;; todo: checksum for IP packet with options!
  714.         add     edi, sizeof.IPv4_header
  715.         DEBUGF  1,"IPv4_output_raw: device=%x\n", ebx
  716.         call    [ebx + NET_DEVICE.transmit]
  717.         ret
  718.  
  719.   .error:
  720.         add     esp, 6
  721.   .arp_error:
  722.         add     esp, 8+4+4
  723.   .too_large:
  724.         DEBUGF  1,"IPv4_output_raw: Failed\n"
  725.         sub     edi, edi
  726.         ret
  727.  
  728.  
  729. ;--------------------------------------------------------
  730. ;
  731. ;
  732. ; IN: dword [esp] = pointer to buffer containing ipv4 packet to be fragmented
  733. ;     dword [esp+4] = buffer size
  734. ;     esi = pointer to ip header in that buffer
  735. ;     ecx = max size of fragments
  736. ;
  737. ; OUT: /
  738. ;
  739. ;--------------------------------------------------------
  740.  
  741. align 4
  742. IPv4_fragment:
  743.  
  744.         DEBUGF 1,"IPv4_fragment\n"
  745.  
  746.         and     ecx, not 111b   ; align 4
  747.  
  748.         cmp     ecx, sizeof.IPv4_header + 8     ; must be able to put at least 8 bytes
  749.         jb      .err2
  750.  
  751.         push    esi ecx
  752.         mov     eax, [esi + IPv4_header.DestinationAddress]
  753.         call    ARP_IP_to_MAC
  754.         pop     ecx esi
  755.         cmp     eax, -1
  756.         jz      .err2
  757.  
  758.         push    ebx
  759.         push    ax
  760.  
  761.         mov     ebx, [NET_DRV_LIST]
  762.         lea     eax, [ebx + ETH_DEVICE.mac]
  763.         push    eax
  764.  
  765.  
  766.         push    esi                             ; ptr to ip header
  767.         sub     ecx, sizeof.IPv4_header         ; substract header size
  768.         push    ecx                             ; max data size
  769.         push    dword 0                         ; offset
  770.  
  771.   .new_fragment:
  772.         DEBUGF 1,"Ipv4_fragment: new fragment"
  773.  
  774.  
  775.         mov     eax, [esp + 3*4]
  776.         lea     ebx, [esp + 4*4]
  777.         mov     di , ETHER_IPv4
  778.         call    ETH_output
  779.  
  780.         cmp     edi, -1
  781.         jz      .err
  782.  
  783. ; copy header
  784.         mov     esi, [esp + 2*4]
  785.         mov     ecx, 5  ; 5 dwords: TODO: use IHL field of the header!
  786.         rep     movsd
  787.  
  788. ; copy data
  789.         mov     esi, [esp + 2*4]
  790.         add     esi, sizeof.IPv4_header
  791.         add     esi, [esp]      ; offset
  792.  
  793.         mov     ecx, [esp + 1*4]
  794.         DEBUGF 1,"IPv4_fragment: copying %u bytes\n", ecx
  795.         rep     movsb
  796.  
  797. ; now, correct header
  798.         mov     ecx, [esp + 1*4]
  799.         add     ecx, sizeof.IPv4_header
  800.         xchg    cl, ch
  801.         mov     [edi + IPv4_header.TotalLength], cx
  802.  
  803.         mov     ecx, [esp]              ; offset
  804.         xchg    cl, ch
  805.  
  806. ;        cmp     dword[esp + 4*4], 0     ; last fragment?;<<<<<<
  807. ;        je      .last_fragment
  808.         or      cx, 1 shl 2             ; more fragments
  809. ;  .last_fragment:
  810.         mov     [edi + IPv4_header.FlagsAndFragmentOffset], cx
  811.  
  812.         mov     [edi + IPv4_header.HeaderChecksum], 0
  813.  
  814.         ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet
  815.         mov     ecx, [esp + 1*4]
  816.  
  817.         push    edx eax
  818.         IPv4_checksum edi
  819.  
  820.         call    [ebx + NET_DEVICE.transmit]
  821.         ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  822.  
  823.         mov     ecx,  [esp+4]
  824.         add     [esp], ecx
  825.  
  826.         mov     ecx, [esp+3*4+6+4]      ; ptr to begin of buff
  827.         add     ecx, [esp+3*4+6+4+4]    ; buff size
  828.         sub     ecx, [esp+2*4]          ; ptr to ip header
  829.         add     ecx, [esp]              ; offset
  830.  
  831.         DEBUGF 1,"Ipv4_fragment: %u bytes remaining\n", ecx
  832.  
  833.         cmp     ecx, [esp+1*4]
  834.         jae     .new_fragment
  835.  
  836.         mov     [esp+4], ecx            ; set fragment size to remaining packet size
  837.         jmp     .new_fragment
  838.  
  839.       .err:
  840.         DEBUGF 1,"Ipv4_fragment: failed\n"
  841.       .done:
  842.         add     esp, 12 + 4 + 6
  843.       .err2:
  844.         DEBUGF 1,"Ipv4_fragment: dumping\n"
  845.         call    kernel_free
  846.         add     esp, 4
  847.  
  848.         ret
  849.  
  850.  
  851.  
  852. ;---------------------------------------------------------------------------
  853. ;
  854. ; IPv4_dest_to_dev
  855. ;
  856. ; IN:   eax = Destination IP
  857. ; OUT:  edi = device id * 4
  858. ;       eax = ip of gateway if nescessary, unchanged otherwise
  859. ;
  860. ;---------------------------------------------------------------------------
  861. align 4
  862. IPv4_dest_to_dev:
  863.  
  864.         cmp     eax, 0xffffffff
  865.         je      .broadcast
  866.  
  867.         xor     edi, edi
  868.         mov     ecx, MAX_NET_DEVICES
  869.   .loop:
  870.         mov     ebx, [IP_LIST+edi]
  871.         and     ebx, [SUBNET_LIST+edi]
  872.         jz      .next
  873.         mov     edx, eax
  874.         and     edx, [SUBNET_LIST+edi]
  875.  
  876.         cmp     ebx, edx
  877.         je      .found_it
  878.   .next:
  879.         add     edi, 4
  880.         dec     ecx
  881.         jnz     .loop
  882.  
  883.   .invalid:
  884.         xor     edi, edi                        ; if none found, use device 0 as default
  885.         mov     eax, [GATEWAY_LIST]
  886.  
  887.   .found_it:
  888.         DEBUGF  1,"IPv4_dest_to_dev: %u\n", edi
  889.         ret
  890.  
  891.   .broadcast:
  892.         xor     edi, edi
  893.         ret
  894.  
  895.  
  896.  
  897. ;---------------------------------------------------------------------------
  898. ;
  899. ; IPv4_get_frgmnt_num
  900. ;
  901. ; IN: /
  902. ; OUT: fragment number in ax
  903. ;
  904. ;---------------------------------------------------------------------------
  905. align 4
  906. IPv4_get_frgmnt_num:
  907.         xor     ax, ax  ;;; TODO: replace this with real code
  908.  
  909.         ret
  910.  
  911.  
  912. ;---------------------------------------------------------------------------
  913. ;
  914. ; IPv4_API
  915. ;
  916. ; This function is called by system function 75
  917. ;
  918. ; IN:  subfunction number in bl
  919. ;      device number in bh
  920. ;      ecx, edx, .. depends on subfunction
  921. ;
  922. ; OUT:
  923. ;
  924. ;---------------------------------------------------------------------------
  925. align 4
  926. IPv4_api:
  927.  
  928.         movzx   eax, bh
  929.         shl     eax, 2
  930.  
  931.         and     ebx, 0x000000ff
  932.         cmp     ebx, .number
  933.         ja      .error
  934.         jmp     dword [.table + 4*ebx]
  935.  
  936.   .table:
  937.         dd      .packets_tx     ; 0
  938.         dd      .packets_rx     ; 1
  939.         dd      .read_ip        ; 2
  940.         dd      .write_ip       ; 3
  941.         dd      .read_dns       ; 4
  942.         dd      .write_dns      ; 5
  943.         dd      .read_subnet    ; 6
  944.         dd      .write_subnet   ; 7
  945.         dd      .read_gateway   ; 8
  946.         dd      .write_gateway  ; 9
  947.   .number = ($ - .table) / 4 - 1
  948.  
  949.   .error:
  950.         mov     eax, -1
  951.         ret
  952.  
  953.   .packets_tx:
  954.         mov     eax, [IP_PACKETS_TX + eax]
  955.         ret
  956.  
  957.   .packets_rx:
  958.         mov     eax, [IP_PACKETS_RX + eax]
  959.         ret
  960.  
  961.   .read_ip:
  962.         mov     eax, [IP_LIST + eax]
  963.         ret
  964.  
  965.   .write_ip:
  966.         mov     [IP_LIST + eax], ecx
  967.  
  968.         ; pre-calculate the local broadcast address
  969.         mov     ebx, [SUBNET_LIST + eax]
  970.         not     ebx
  971.         or      ecx, ebx
  972.         mov     [BROADCAST_LIST + eax], ecx
  973.  
  974.         xor     eax, eax
  975.         ret
  976.  
  977.   .read_dns:
  978.         mov     eax, [DNS_LIST + eax]
  979.         ret
  980.  
  981.   .write_dns:
  982.         mov     [DNS_LIST + eax], ecx
  983.         xor     eax, eax
  984.         ret
  985.  
  986.   .read_subnet:
  987.         mov     eax, [SUBNET_LIST + eax]
  988.         ret
  989.  
  990.   .write_subnet:
  991.         mov     [SUBNET_LIST + eax], ecx
  992.  
  993.         ; pre-calculate the local broadcast address
  994.         mov     ebx, [IP_LIST + eax]
  995.         not     ecx
  996.         or      ecx, ebx
  997.         mov     [BROADCAST_LIST + eax], ecx
  998.  
  999.         xor     eax, eax
  1000.         ret
  1001.  
  1002.   .read_gateway:
  1003.         mov     eax, [GATEWAY_LIST + eax]
  1004.         ret
  1005.  
  1006.   .write_gateway:
  1007.         mov     [GATEWAY_LIST + eax], ecx
  1008.         xor     eax, eax
  1009.         ret