2013-05-28 21:34:26 +04:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; ;;
|
2015-01-08 23:10:22 +03:00
|
|
|
;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;;
|
2013-05-28 21:34:26 +04:00
|
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
|
|
;; ;;
|
|
|
|
;; UDP.INC ;;
|
|
|
|
;; ;;
|
|
|
|
;; Part of the tcp/ip network stack for KolibriOS ;;
|
|
|
|
;; ;;
|
|
|
|
;; Written by hidnplayr@kolibrios.org ;;
|
|
|
|
;; ;;
|
|
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
|
|
;; Version 2, June 1991 ;;
|
|
|
|
;; ;;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2014-04-18 01:19:45 +04:00
|
|
|
$Revision$
|
2011-10-15 01:38:50 +04:00
|
|
|
|
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
struct UDP_header
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
SourcePort dw ?
|
|
|
|
DestinationPort dw ?
|
|
|
|
Length dw ? ; Length of (UDP Header + Data)
|
|
|
|
Checksum dw ?
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
ends
|
|
|
|
|
|
|
|
|
|
|
|
uglobal
|
2013-06-24 15:39:05 +04:00
|
|
|
align 4
|
|
|
|
|
2013-06-04 18:12:37 +04:00
|
|
|
UDP_PACKETS_TX rd NET_DEVICES_MAX
|
|
|
|
UDP_PACKETS_RX rd NET_DEVICES_MAX
|
2013-06-24 15:39:05 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
endg
|
|
|
|
|
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
2015-12-27 18:37:31 +03:00
|
|
|
; udp_init: This function resets all UDP variables ;
|
2015-12-17 00:20:53 +03:00
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2015-12-27 18:37:31 +03:00
|
|
|
macro udp_init {
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
xor eax, eax
|
|
|
|
mov edi, UDP_PACKETS_TX
|
2013-06-04 18:12:37 +04:00
|
|
|
mov ecx, 2*NET_DEVICES_MAX
|
2013-06-27 03:35:43 +04:00
|
|
|
rep stosd
|
2011-10-15 01:38:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-27 18:37:31 +03:00
|
|
|
macro udp_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
; Pseudoheader
|
|
|
|
mov edx, IP_PROTO_UDP
|
|
|
|
|
2015-10-11 21:23:40 +03:00
|
|
|
add dl, byte[IP1+1]
|
|
|
|
adc dh, byte[IP1+0]
|
|
|
|
adc dl, byte[IP1+3]
|
|
|
|
adc dh, byte[IP1+2]
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2015-10-11 21:23:40 +03:00
|
|
|
adc dl, byte[IP2+1]
|
|
|
|
adc dh, byte[IP2+0]
|
|
|
|
adc dl, byte[IP2+3]
|
|
|
|
adc dh, byte[IP2+2]
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
adc dl, cl ; byte[esi+UDP_header.Length+1]
|
|
|
|
adc dh, ch ; byte[esi+UDP_header.Length+0]
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
; Done with pseudoheader, now do real header
|
|
|
|
adc dl, byte[esi+UDP_header.SourcePort+1]
|
|
|
|
adc dh, byte[esi+UDP_header.SourcePort+0]
|
|
|
|
|
|
|
|
adc dl, byte[esi+UDP_header.DestinationPort+1]
|
|
|
|
adc dh, byte[esi+UDP_header.DestinationPort+0]
|
|
|
|
|
|
|
|
adc dl, byte[esi+UDP_header.Length+1]
|
|
|
|
adc dh, byte[esi+UDP_header.Length+0]
|
|
|
|
|
|
|
|
adc edx, 0
|
|
|
|
|
|
|
|
; Done with header, now do data
|
|
|
|
push esi
|
|
|
|
movzx ecx, [esi+UDP_header.Length]
|
|
|
|
rol cx , 8
|
|
|
|
sub cx , sizeof.UDP_header
|
|
|
|
add esi, sizeof.UDP_header
|
|
|
|
|
|
|
|
call checksum_1
|
|
|
|
call checksum_2
|
|
|
|
pop esi
|
|
|
|
|
|
|
|
add [esi+UDP_header.Checksum], dx ; this final instruction will set or clear ZF :)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
2015-12-27 18:37:31 +03:00
|
|
|
; udp_input: Inject the UDP data in the application sockets. ;
|
2015-12-17 00:20:53 +03:00
|
|
|
; ;
|
|
|
|
; IN: [esp] = ptr to buffer ;
|
|
|
|
; ebx = ptr to device struct ;
|
|
|
|
; ecx = UDP packet size ;
|
|
|
|
; edx = ptr to IPv4 header ;
|
|
|
|
; esi = ptr to UDP packet data ;
|
|
|
|
; edi = interface number*4 ;
|
|
|
|
; ;
|
|
|
|
; OUT: / ;
|
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2013-05-28 21:34:26 +04:00
|
|
|
align 4
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_input:
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: size=%u\n", ecx
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
; First validate, checksum
|
|
|
|
|
|
|
|
neg [esi + UDP_header.Checksum] ; substract checksum from 0
|
|
|
|
jz .no_checksum ; if checksum is zero, it is considered valid
|
|
|
|
|
|
|
|
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2015-10-11 21:23:40 +03:00
|
|
|
mov eax, edx
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_checksum (eax+IPv4_header.SourceAddress), (eax+IPv4_header.DestinationAddress)
|
2013-05-28 21:34:26 +04:00
|
|
|
jnz .checksum_mismatch
|
2011-10-15 01:38:50 +04:00
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
.no_checksum:
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: checksum ok\n"
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
; Convert length to little endian
|
|
|
|
|
|
|
|
rol [esi + UDP_header.Length], 8
|
2011-10-15 01:38:50 +04:00
|
|
|
|
|
|
|
; Look for a socket where
|
|
|
|
; IP Packet UDP Destination Port = local Port
|
|
|
|
; IP Packet SA = Remote IP
|
|
|
|
|
2013-06-12 14:21:41 +04:00
|
|
|
pusha
|
|
|
|
mov ecx, socket_mutex
|
|
|
|
call mutex_lock
|
|
|
|
popa
|
|
|
|
|
2013-05-28 21:34:26 +04:00
|
|
|
mov cx, [esi + UDP_header.SourcePort]
|
|
|
|
mov dx, [esi + UDP_header.DestinationPort]
|
|
|
|
mov eax, net_sockets
|
2011-10-15 01:38:50 +04:00
|
|
|
.next_socket:
|
2013-05-28 21:34:26 +04:00
|
|
|
mov eax, [eax + SOCKET.NextPtr]
|
|
|
|
or eax, eax
|
2015-03-18 00:50:29 +03:00
|
|
|
jz .unlock_dump
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
cmp [eax + SOCKET.Domain], AF_INET4
|
|
|
|
jne .next_socket
|
|
|
|
|
|
|
|
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
|
|
|
|
jne .next_socket
|
|
|
|
|
|
|
|
cmp [eax + UDP_SOCKET.LocalPort], dx
|
|
|
|
jne .next_socket
|
|
|
|
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: socket=%x\n", eax
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2013-06-12 14:21:41 +04:00
|
|
|
pusha
|
|
|
|
mov ecx, socket_mutex
|
|
|
|
call mutex_unlock
|
|
|
|
popa
|
|
|
|
|
2015-10-11 21:23:40 +03:00
|
|
|
;;; TODO: when packet is processed, check more sockets?!
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2015-10-11 21:23:40 +03:00
|
|
|
; FIXME: check remote IP if possible
|
|
|
|
;
|
2013-05-28 21:34:26 +04:00
|
|
|
; cmp [eax + IP_SOCKET.RemoteIP], 0xffffffff
|
|
|
|
; je @f
|
2015-10-11 21:23:40 +03:00
|
|
|
; cmp [eax + IP_SOCKET.RemoteIP],
|
2013-05-28 21:34:26 +04:00
|
|
|
; jne .next_socket
|
|
|
|
; @@:
|
|
|
|
|
2013-10-15 14:35:08 +04:00
|
|
|
cmp [eax + UDP_SOCKET.RemotePort], 0
|
2013-05-28 21:34:26 +04:00
|
|
|
je .updateport
|
|
|
|
|
|
|
|
cmp [eax + UDP_SOCKET.RemotePort], cx
|
|
|
|
jne .dump
|
|
|
|
|
|
|
|
pusha
|
|
|
|
lea ecx, [eax + SOCKET.mutex]
|
|
|
|
call mutex_lock
|
|
|
|
popa
|
|
|
|
|
|
|
|
.updatesock:
|
2013-06-11 14:46:29 +04:00
|
|
|
inc [UDP_PACKETS_RX + edi]
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
movzx ecx, [esi + UDP_header.Length]
|
|
|
|
sub ecx, sizeof.UDP_header
|
|
|
|
add esi, sizeof.UDP_header
|
|
|
|
|
2015-12-27 18:37:31 +03:00
|
|
|
jmp socket_input
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
.updateport:
|
|
|
|
pusha
|
|
|
|
lea ecx, [eax + SOCKET.mutex]
|
|
|
|
call mutex_lock
|
|
|
|
popa
|
|
|
|
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: new remote port=%x\n", cx ; FIXME: find a way to print big endian values with debugf
|
2013-05-28 21:34:26 +04:00
|
|
|
mov [eax + UDP_SOCKET.RemotePort], cx
|
|
|
|
jmp .updatesock
|
|
|
|
|
2015-03-18 00:50:29 +03:00
|
|
|
.unlock_dump:
|
2013-06-12 14:21:41 +04:00
|
|
|
pusha
|
|
|
|
mov ecx, socket_mutex
|
|
|
|
call mutex_unlock
|
|
|
|
popa
|
|
|
|
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: no socket found\n"
|
|
|
|
jmp .dump
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
.checksum_mismatch:
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: checksum mismatch\n"
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
.dump:
|
2015-03-18 00:50:29 +03:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: dumping\n"
|
2015-12-27 18:37:31 +03:00
|
|
|
call net_buff_free
|
2013-05-28 21:34:26 +04:00
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
2015-12-27 18:37:31 +03:00
|
|
|
; udp_output: Create an UDP packet. ;
|
2015-12-17 00:20:53 +03:00
|
|
|
; ;
|
|
|
|
; IN: eax = socket pointer ;
|
|
|
|
; ecx = number of bytes to send ;
|
|
|
|
; esi = pointer to data ;
|
|
|
|
; ;
|
|
|
|
; OUT: eax = -1 on error ;
|
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
align 4
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_output:
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: socket=%x bytes=%u data_ptr=%x\n", eax, ecx, esi
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
mov dx, [eax + UDP_SOCKET.RemotePort]
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: remote port=%x, ", dx ; FIXME: find a way to print big endian values with debugf
|
2013-05-28 21:34:26 +04:00
|
|
|
rol edx, 16
|
|
|
|
mov dx, [eax + UDP_SOCKET.LocalPort]
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "local port=%x\n", dx
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2015-03-18 00:50:29 +03:00
|
|
|
sub esp, 4 ; Data ptr will be placed here
|
2013-05-28 21:34:26 +04:00
|
|
|
push edx esi
|
2015-07-20 14:13:42 +03:00
|
|
|
mov ebx, [eax + IP_SOCKET.device]
|
2015-10-11 21:23:40 +03:00
|
|
|
mov edx, [eax + IP_SOCKET.LocalIP]
|
|
|
|
mov edi, [eax + IP_SOCKET.RemoteIP]
|
|
|
|
mov al, [eax + IP_SOCKET.ttl]
|
|
|
|
mov ah, IP_PROTO_UDP
|
2013-05-28 21:34:26 +04:00
|
|
|
add ecx, sizeof.UDP_header
|
2015-12-27 18:37:31 +03:00
|
|
|
call ipv4_output
|
2013-05-28 21:34:26 +04:00
|
|
|
jz .fail
|
|
|
|
mov [esp + 8], eax ; pointer to buffer start
|
|
|
|
|
|
|
|
mov [edi + UDP_header.Length], cx
|
|
|
|
rol [edi + UDP_header.Length], 8
|
|
|
|
|
|
|
|
pop esi
|
|
|
|
push edi ecx
|
|
|
|
sub ecx, sizeof.UDP_header
|
|
|
|
add edi, sizeof.UDP_header
|
|
|
|
shr ecx, 2
|
2013-06-27 03:35:43 +04:00
|
|
|
rep movsd
|
2013-05-28 21:34:26 +04:00
|
|
|
mov ecx, [esp]
|
|
|
|
and ecx, 3
|
2013-06-27 03:35:43 +04:00
|
|
|
rep movsb
|
2013-05-28 21:34:26 +04:00
|
|
|
pop ecx edi
|
|
|
|
|
|
|
|
pop dword [edi + UDP_header.SourcePort]
|
|
|
|
|
|
|
|
; Checksum
|
|
|
|
mov esi, edi
|
|
|
|
mov [edi + UDP_header.Checksum], 0
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_checksum (edi-4), (edi-8) ; FIXME: IPv4 packet could have options..
|
2013-05-28 21:34:26 +04:00
|
|
|
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: sending with device %x\n", ebx
|
2013-05-28 21:34:26 +04:00
|
|
|
call [ebx + NET_DEVICE.transmit]
|
|
|
|
test eax, eax
|
|
|
|
jnz @f
|
2015-12-27 18:37:31 +03:00
|
|
|
call net_ptr_to_num4
|
2013-06-11 14:46:29 +04:00
|
|
|
inc [UDP_PACKETS_TX + edi]
|
2013-05-28 21:34:26 +04:00
|
|
|
@@:
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
.fail:
|
2013-05-28 23:19:23 +04:00
|
|
|
DEBUGF DEBUG_NETWORK_ERROR, "UDP_output: failed\n"
|
2013-05-28 21:34:26 +04:00
|
|
|
add esp, 4+4+8
|
|
|
|
or eax, -1
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-15 14:35:08 +04:00
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
2015-12-27 18:37:31 +03:00
|
|
|
; udp_connect ;
|
2015-12-17 00:20:53 +03:00
|
|
|
; ;
|
|
|
|
; IN: eax = socket pointer ;
|
|
|
|
; ;
|
|
|
|
; OUT: eax = 0 on success ;
|
|
|
|
; eax = -1 on error ;
|
|
|
|
; ebx = error code on error ;
|
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2013-10-15 14:35:08 +04:00
|
|
|
align 4
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_connect:
|
2013-10-15 14:35:08 +04:00
|
|
|
|
|
|
|
test [eax + SOCKET.state], SS_ISCONNECTED
|
|
|
|
jz @f
|
2015-12-27 18:37:31 +03:00
|
|
|
call udp_disconnect
|
2013-10-15 14:35:08 +04:00
|
|
|
@@:
|
|
|
|
|
2013-10-15 20:18:58 +04:00
|
|
|
push eax edx
|
2013-10-15 14:35:08 +04:00
|
|
|
lea ecx, [eax + SOCKET.mutex]
|
|
|
|
call mutex_lock
|
2013-10-15 20:18:58 +04:00
|
|
|
pop edx eax
|
2013-10-15 14:35:08 +04:00
|
|
|
|
|
|
|
; Fill in local IP
|
|
|
|
cmp [eax + IP_SOCKET.LocalIP], 0
|
|
|
|
jne @f
|
|
|
|
push [IP_LIST + 4] ; FIXME: use correct local IP
|
|
|
|
pop [eax + IP_SOCKET.LocalIP]
|
|
|
|
|
|
|
|
; Fill in remote port and IP, overwriting eventually previous values
|
|
|
|
pushw [edx + 2]
|
|
|
|
pop [eax + UDP_SOCKET.RemotePort]
|
|
|
|
|
|
|
|
pushd [edx + 4]
|
|
|
|
pop [eax + IP_SOCKET.RemoteIP]
|
|
|
|
|
|
|
|
; Find a local port, if user didnt define one
|
|
|
|
cmp [eax + UDP_SOCKET.LocalPort], 0
|
|
|
|
jne @f
|
2015-12-27 18:37:31 +03:00
|
|
|
call socket_find_port
|
2013-10-15 14:35:08 +04:00
|
|
|
@@:
|
|
|
|
|
|
|
|
push eax
|
|
|
|
lea ecx, [eax + SOCKET.mutex]
|
|
|
|
call mutex_unlock
|
|
|
|
pop eax
|
|
|
|
|
2015-12-27 18:37:31 +03:00
|
|
|
call socket_is_connected
|
2013-10-15 14:35:08 +04:00
|
|
|
|
|
|
|
xor eax, eax
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
|
|
|
; UDP_disconnect ;
|
|
|
|
; ;
|
|
|
|
; IN: eax = socket pointer ;
|
|
|
|
; ;
|
|
|
|
; OUT: eax = socket pointer ;
|
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2013-10-15 14:35:08 +04:00
|
|
|
align 4
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_disconnect:
|
2013-10-15 14:35:08 +04:00
|
|
|
|
|
|
|
; TODO: remove the pending received data
|
|
|
|
|
2015-12-27 18:37:31 +03:00
|
|
|
call socket_is_disconnected
|
2013-10-15 14:35:08 +04:00
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-17 00:20:53 +03:00
|
|
|
;-----------------------------------------------------------------;
|
|
|
|
; ;
|
2015-12-27 18:37:31 +03:00
|
|
|
; UDP_api: Part of system function 76 ;
|
2015-12-17 00:20:53 +03:00
|
|
|
; ;
|
|
|
|
; IN: bl = subfunction number in bl ;
|
|
|
|
; bh = device number in bh ;
|
|
|
|
; ecx, edx, .. depends on subfunction ;
|
|
|
|
; ;
|
|
|
|
; OUT: depends on subfunction ;
|
|
|
|
; ;
|
|
|
|
;-----------------------------------------------------------------;
|
2013-05-28 21:34:26 +04:00
|
|
|
align 4
|
2015-12-27 18:37:31 +03:00
|
|
|
udp_api:
|
2013-05-28 21:34:26 +04:00
|
|
|
|
|
|
|
movzx eax, bh
|
|
|
|
shl eax, 2
|
|
|
|
|
|
|
|
test bl, bl
|
|
|
|
jz .packets_tx ; 0
|
|
|
|
dec bl
|
|
|
|
jz .packets_rx ; 1
|
|
|
|
|
|
|
|
.error:
|
|
|
|
mov eax, -1
|
|
|
|
ret
|
|
|
|
|
|
|
|
.packets_tx:
|
|
|
|
mov eax, [UDP_PACKETS_TX + eax]
|
|
|
|
ret
|
|
|
|
|
|
|
|
.packets_rx:
|
|
|
|
mov eax, [UDP_PACKETS_RX + eax]
|
2011-10-15 01:38:50 +04:00
|
|
|
ret
|