Subversion Repositories Kolibri OS

Rev

Rev 3589 | Rev 3725 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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