Closed SimonPVS closed 2 years ago
Do you have an example of how to use it?
hi @SimonPVS You can follow these steps:
Then call the following function: setsockopt(socketid, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));
struct msghdr msg; struct cmsghdr *cmsg; 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(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.
@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);
}
}`
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 ( 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);
}
}
hi @negativekelvin
You can follow this process to get the address,I will update an example in IDF later.
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.
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.