espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.46k stars 7.25k forks source link

UDP packet destination address (IDFGH-5400) #7146

Closed SimonPVS closed 2 years ago

SimonPVS commented 3 years ago

Is it possible to retrieve the destination address from an UDP packet? I have one UDP socket. This socket receives broadcast, multicast and unicast, but the ESP should perform a different action depending on the destination it was send to (uni, broad or multi).

In the UDP examples provided by esspressif, only the source address can be found, using the recvfrom() function. So it would be very nice if the destination address was also provided to make a destinction between a unicast, multicast or broadcast.

negativekelvin commented 3 years ago

https://github.com/espressif/esp-idf/issues/1460

SimonPVS commented 3 years ago

Do you have an example of how to use it?

xueyunfei998 commented 3 years ago

hi @SimonPVS You can follow these steps:

lwip_recvmsg(s, &msg, 0); cmsg = CMSG_FIRSTHDR(&msg); pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); pktinfo->ipi_addr----->>This variable holds the original address.

This function is supported in IDF4.0 and above. You can follow the above steps to achieve this function.

SimonPVS commented 3 years ago

@xueyunfei998 Thanks!

But when can i call the 'lwip_recvmsg' ? Can this be used in combination with the recvfrom function? And what is the iov parameter used for?

I know have this: `static void mcast_receive_task(void *pvParameters) { while(EthernetConnected == 0) // Wait until ethernet connection { vTaskDelay(100 / portTICK_PERIOD_MS); } while(1) {

    create_multicast_ipv4_socket();

    if (udp_sock < 0) {
        ESP_LOGE(TAG, "Failed to create IPv4 multicast socket");
    }

    if (udp_sock < 0) {
        // Nothing to do!
        vTaskDelay(5 / portTICK_PERIOD_MS);
        continue;
    }

    // set destination multicast addresses for sending from these sockets

    struct sockaddr_in sdestv4 = {
        .sin_family = PF_INET,
        .sin_port = htons(UDP_VU_PORT),
    };
    // We know this inet_aton will pass because we did it above already
    inet_aton(UDP_MULTI_IP, &sdestv4.sin_addr.s_addr);

    // Loop waiting for UDP received, and sending UDP packets if we don't
    // see any.
    int err = 1;
    while (err > 0) {
        struct timeval tv = {
            .tv_sec = 10,
            .tv_usec = 0,
        };
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(udp_sock, &rfds);

        //------------- !!!!!! -------------------
        // If there is no data,
        // the calling RTOS task will be held in the blocked state (other tasks can execute)
        // That is why no task-delay is required here
        //------------- !!!!!! -------------------
        int s = select(udp_sock + 1, &rfds, NULL, NULL, &tv);
        if (s < 0) {
            ESP_LOGE(TAG, "Select failed: errno %d", errno);
            err = -1;
            break;
        }
        else if (s > 0) {
            if (FD_ISSET(udp_sock, &rfds)) {
                // Incoming datagram received
                char recvbuf[48];
                char raddr_name[32] = { 0 };

                struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6
                socklen_t socklen = sizeof(raddr);

                int len = recvfrom(udp_sock, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&raddr, &socklen);

                struct msghdr msg;
                struct cmsghdr *cmsg;
                struct iovec iov;
                u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
                msg.msg_control = cmsg_buf;
                msg.msg_controllen = sizeof(cmsg_buf);
                msg.msg_flags = 0;
                msg.msg_iov = &iov;
                msg.msg_iovlen = 1;
                msg.msg_name = NULL;
                msg.msg_namelen = 0;

                lwip_recvmsg(udp_sock, &msg, 0);
                cmsg = CMSG_FIRSTHDR(&msg);
                struct in_pktinfo * pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
                inet_ntoa_r(((struct in_pktinfo *)&pktinfo)->ipi_addr, raddr_name, sizeof(raddr_name)-1);

                ESP_LOGI(TAG, "ADDRESS: %s", raddr_name);

                if (len < 0) {
                    ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
                    err = -1;
                    break;
                }

                // Get the sender's address as a string
                if (raddr.ss_family == PF_INET) {
                    inet_ntoa_r(((struct sockaddr_in *)&raddr)->sin_addr, raddr_name, sizeof(raddr_name)-1);
                }

                recvbuf[len] = 0; // Null-terminate whatever we received and treat like a string...
                ESP_LOGI(TAG, "received %d bytes from %s: %s", len, raddr_name, recvbuf);

                //------ ADDED FOR TESTING -----------
                if(strcmp(recvbuf, "broad") == 0)
                {
                    UDP_sendBroadcast( UDP_VU_PORT, "TEST BROAD");
                }
                else if(strcmp(recvbuf, "multi") == 0)
                {
                    UDP_sendMulticast( UDP_VU_PORT, "TEST MULTI");
                }
                else if(strcmp(recvbuf, "uni") == 0)
                {
                    UDP_sendUnicast( UDP_VU_PORT, "TEST UNI");
                }
                else if(strcmp(recvbuf, "drop") == 0)
                {
                    UDP_releaseMultigroup();
                }
            }
        }
    }

    ESP_LOGE(TAG, "Shutting down socket and restarting...");
    shutdown(udp_sock, 0);
    close(udp_sock);

}

}`

xueyunfei998 commented 2 years ago

struct iovec iov; struct msghdr msg; struct cmsghdr cmsgtmp; struct in_pktinfo pktinfo; u8_t rcv_buf[1500]; u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; int sockfd;

struct sockaddr_in dstaddr;

iov.iov_base = rcv_buf; iov.iov_len = sizeof(rcv_buf); msg.msg_control = cmsg_buf; msg.msg_controllen = sizeof(cmsg_buf); msg.msg_flags = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = &dstaddr;

sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

int enable = 1;

setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));

recvmsg(sockfd, &msg, 0);

for ( cmsgtmp = CMSG_FIRSTHDR(&msg); cmsgtmp != NULL; cmsgtmp = CMSG_NXTHDR(&msg, cmsgtmp) ) {

if defined(IP_PKTINFO)

        if ( cmsgtmp->cmsg_level == IPPROTO_IP && cmsgtmp->cmsg_type == IP_PKTINFO ) {
            struct in_pktinfo *pktinfo;
            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsgtmp);
            printf("recv ip: %s, ifindex: %d\n", inet_ntoa(pktinfo->ipi_addr), pktinfo->ipi_ifindex);
        }

endif

}

hi @negativekelvin

You can follow this process to get the address,I will update an example in IDF later.

Alvin1Zhang commented 2 years ago

Thanks for reporting, we also have a fix https://github.com/espressif/esp-idf/commit/96ff900d7d2f4b47a4801e0605b1b4337d48f2ef, feel free to reopen if the issue happens. Thanks.