如何在 nasm 中使用 recvfrom 获取 UDP 数据包的源 IP 地址?

How to get source IP address of a UDP packet using recvfrom in nasm?

我正在 nasm 中编写 UDP 套接字,并且我正在使用 recvfrom 系统调用从客户端接收 UDP 数据包。

我可以成功发送消息,但我想向客户发回一个答复。 问题是我无法提取数据包的源IP地址,所以无法使用sendto系统调用。

这是UDP服务器的代码:

%include "../StandardLibrary/standardlib.inc"
%include "./network.inc"

PORT_NUMBER equ 4096

%macro printError 3
    print %1
    printLineInt %2, %3
%endmacro

%macro clearBuffer 1
    getLength %1

    mov r12, 0
    %%loop:
        mov [%1 + r12], byte 0
        inc r12
        cmp r11, r12
        jne %%loop

%endmacro

section .data
    sock_addr:
    istruc sockaddr_in
        at sockaddr_in.sin_family, dw AF_INET
        at sockaddr_in.sin_port, dw htons(PORT_NUMBER)
        at sockaddr_in.in_addr, db 00h, 00h, 00h, 00h
        at sockaddr_in.sin_zero, db 0, 0, 0, 0, 0, 0, 0, 0
    iend

    client_sockaddr:
    istruc sockaddr
        at sockaddr.sa_family, dw AF_INET
        at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0
    iend

    ;Messaggi di successo
    so_creation_success db "Socket creato con successo", 0
    so_binding_success db "Binding del socket eseguito con successo", 0
    so_started_capturing db "Socket in ascolto di messaggi...", 0

    ;Messaggi di errore
    so_creation_error db "Errore nella creazione del socket: ", 0
    so_binding_error db "Errore nel binding del socket: ", 0
    so_capture_error db "Errore nella ricezione del messaggio: ", 0
    so_sending_error db "Errore nell'invio del messaggio: ", 0

section .bss
    buffer resb 100
    socket_fd resw 1
    error_code resq 1
    tmp resb 10

section .text
    global _start

_start:
    ;1) Creazione del socket
    mov rax, SYS_SOCKET
    mov rdi, AF_INET
    mov rsi, SOCK_DGRAM
    mov rdx, 0
    syscall

    cmp rax, -1
    jle _socket_error

    ;Carica nello stack il file descriptor
    mov [socket_fd], rax
    printLine so_creation_success

    ;2) Binding del socket
    mov rax, SYS_BIND
    mov rdi, [socket_fd]
    mov rsi, sock_addr
    mov rdx, sockaddr_in_size
    syscall

    cmp rax, -1
    jle  _binding_error
    printLine so_binding_success

    ;3) Ricezione dei messaggi
    _listen:
        mov rax, SYS_RECVFROM
        mov rdi, [socket_fd]
        mov rsi, buffer
        mov rdx, 20
        mov r10, 0
        mov r8, client_sockaddr
        mov r9, 16
        syscall

        cmp rax, -1          ;Controlla che non ci siano stati errori
        jle _capture_error   
        cmp rax, 1          ;Controlla che non sia stato ricevuto un pcchetto vuoto
        jle _exit           ;è definito vuoto anche un pacchetto con un solo carattere

        ;Messaggio ricevuto con successo
        print buffer
        clearBuffer buffer  ;Svuota il buffer
        jmp _listen

    ;Il programma è terminato con successo
    _exit:
        exit

    ;Errore nella creazione del socket
    _socket_error:
        neg rax
        mov [error_code], rax
        printError so_creation_error, [error_code], tmp
        exit [error_code]

    ;Errore durante il binding del socket
    _binding_error:
        neg rax
        mov [error_code], rax
        printError so_binding_error, [error_code], tmp
        exit [error_code]

    ;Errore durante la ricezione del messaggio
    _capture_error:
        neg rax
        mov [error_code], rax
        printError so_capture_error, [error_code], tmp
        exit [error_code]

在 network.inc 我有:

SYS_SOCKET          equ 41  ;Id system call per la creazione del socket
SYS_SENDTO          equ 44  ;Id system call per l'invio di datagram UDP
SYS_RECVFROM        equ 45  ;Id system call per la ricezione di datagram UDP
SYS_BIND            equ 49  ;Id system call per legare un socket ad un indirizzo IP (o ad una famiglia)

AF_INET             equ 2   ;Rete IPv4
SOCK_DGRAM          equ 2   ;Id del protocollo UDP
INADDR_ANY          equ 0   ;Indica che il socket accetta comunicazioni da qualsiasi indirizzo IP

;Traduce x in un valore di tipo network byte order
%define htons(x) ((x >> 8) & 0xFF) | ((x & 0xFF) << 8)

;Rappresenta un indirizzo IPv4
struc in_addr
    .s_addr:        resb 4  ;Valore dei 4 ottetti
endstruc

;Rappresenta la struttura di un socket
struc sockaddr_in
    .sin_family:    resw 1  ;Id del tipo di indirizzo
    .sin_port:      resw 1  ;Numero di porta
    .in_addr:       resb 4  ;Indirizzo IP
    .sin_zero:      resb 8  ;Byte di rimepimento
endstruc

struc sockaddr
    .sa_family      resw 1
    .sa_data        resb 14
endstruc

作为 UDP 客户端,我正在使用 netcat 命令:netcat -u 127.0.0.1 4096

您可以在 SYS_RECVFROM according to the documentation 中传递指向 sockaddr 结构的指针:

mov r8, 0
mov r9, 0

然后通过 sendto 系统调用使用填充结构。

UPD

sockaddrC 中具有以下形状:

struct sockaddr {
    unsigned short int sa_family;
    char sa_data[14];
};

我想,在 NASM 中看起来像这样:

struc sockaddr
    .sa_family resw 1
    .sa_data db 14
endstruc


client_sock_addr:
istruc sockaddr
    at sockaddr.sa_family, dw AF_INET
    at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
iend

UPD #2

我注意到您正在为套接字分配 2 个字节。实际上,套接字必须是 int:

socket_fd resd 1

接下来:您必须传递给 r9 引用而不是值

section .data
    ; ...
    client_sockaddr_len dd 16
    ; ...

; ...
mov rax, SYS_RECVFROM
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 20
mov r10, 0
mov r8, client_sockaddr
mov r9, client_sockaddr_len
syscall

终于可以sendto

mov rax, SYS_SENDTO
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 50
mov r10, 0
mov r8, client_sockaddr
mov r9, [client_sockaddr_len]
syscall