socketsudpipnasmrecvfrom

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


I'm writing a UDP socket in nasm and I'm using the recvfrom system call to receive a UDP packet from a client.

I can successfully get the message sent, but I would like to send back to the client an answer. The problem is that I can't extract the source IP address of the packet, so I can't use the sendto system call.

This is the code of the UDP server:

%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]

And on network.inc I have:

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

As UDP client I'm using the netcat command: netcat -u 127.0.0.1 4096


Solution

  • You can pass a pointer to your sockaddr structure in SYS_RECVFROM according to the documentation here:

    mov r8, 0
    mov r9, 0
    

    And then use filled structure with sendto syscall.

    UPD

    sockaddr has following shape in C:

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

    I think, it looks something like this in 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

    I've noticed that you're allocating 2 bytes for the socket. Actually, sockets must be int:

    socket_fd resd 1
    

    Next thing: you have to pass to r9 reference rather than value

    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
    

    And finally you can 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