Post

Replies

Boosts

Views

Activity

Reply to ICMP reply not received
OK, egg on my face, I have figured this out. Having not worked on Unix-y systems much, I neglected to understand what the first argument to select() was. In Windows, it turns out to be basically ignored. In Unix, it is the max number of file descriptors passed in the FD_SET. So, basically, s+1 where s is the file descriptor of the socket. Obviously rookie mistake. DOH. Thanks for the help and while it took me some personal head scratching I learned something about unix. :-)
1d
Reply to ICMP reply not received
I have kind of figured out the problem, but I don't "understand" it. I switched the code from making a select() followed by recv(), to just calling recvfrom(). The select() call always times out. But recvfrom() works! However, my understanding is the semantics of recvfrom() are to block until there is something to read. But if I send a ping and it doesn't work for whatever of many possible reasons, I need to not wait forever and time out after a few seconds of waiting. Key semantic question: why does select not wake up when there is (or should be?) a data packet there? I inserted some sleep() time before the recvfrom() just to make sure it would receive something even if it was buffered somewhere. Also just FWIW, I confirmed that the desired packets were being received on wire by using the following in a separate shell window: $ sudo tcpdump 'icmp' This saw my packets get sent and received, and led me to try other things.
4d
Reply to ICMP reply not received
Thanks for the thought, but... I don't think connect does anything except tell the socket what address to send to. Supporting this theory, I did switch to skip calling connect(), followed by using sendto() with the destination address instead. No change in behavior, both platforms act the same as before. It still kinda feels like somewhere there's some kind of OS filtering maybe. I did turn off the System Settings->Network->Firewall switch and run again, but no change. Might the OS filter inbound ICMP packets at a low level in some way? Or maybe the outbound ones, actually, it is possible it never got sent. Thanks for the idea on getaddrinfo(), will change that (haven't tried yet tho). But it feels tangential to whatever is actually going on.
5d
Reply to ICMP reply not received
Hi Quinn, thanks for the kind attention and example. I looked it over, and while I'm not too up on Swift I don't quite know why mine wouldn't work as I think(?) the basic network calls are similar. Though... there are some higher level wrappers that I'm not highly familiar with. I boiled my C++ (but barely) code down to its minimum to illustrate the issue. I compile and run this on MacOS with g++, as well as Windows with g++ (using mingw64 tools). Works fine on Windows, both on Win11/Parallels on my Mac as well as my 12+ yr old Win10 machine. I've also pasted in snapshots of the output. On MacOS it always times out in the select(), i.e. never receives anything. MacOS output (terminal) ~/source/test % g++ pingtest.cpp ~/source/test % a.out www.yahoo.com connecting to www.yahoo.com (69.147.88.8) Timed out with no reply received ~/source/test % Windows output (cmd), built on Win11/Parallels but same output when the exe is copied to and run on old Win10 M:\source\test>g++ -D_WINDOWS pingtest.cpp -lws2_32 M:\source\test>a.exe www.yahoo.com connecting to www.yahoo.com (69.147.88.8) Reply was received, 0.013000 secs M:\source\test> And the C++ source code // pingtest.cpp: MacOS times out on select() (line 165) #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <assert.h> #include <unistd.h> #include <time.h> #include <errno.h> #include <string.h> #ifndef _WINDOWS // Compiling on MacOS needs these for various definitions #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> // windows-y terms for compiling in unix land typedef int SOCKET; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 // SOCK_DGRAM means no need to run as sudo on MacOS // (but select() times out either way, still illustrating the problem) #define MY_SOCK_TYPE SOCK_DGRAM #else // _WINDOWS #include <winsock.h> // create a RAW socket b/c get error WSAPROTONOSUPPORT w/SOCK_DGRAM #define MY_SOCK_TYPE SOCK_RAW #endif // _WINDOWS #define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0 // The IP header struct IpHeader { unsigned char h_len_version; // Whoa, g++ doesn't always pack bit fields // unsigned int h_len:4; // length of the header // unsigned int version:4; // Version of IP unsigned char tos; // Type of service unsigned short total_len; // total length of the packet unsigned short ident; // unique identifier unsigned short frag_and_flags; // flags unsigned char ttl; unsigned char proto; // protocol (TCP, UDP etc) unsigned short checksum; // IP checksum unsigned int sourceIP; unsigned int destIP; }; // // ICMP header // struct IcmpHeader { unsigned char i_type; unsigned char i_code; // type sub code unsigned short i_cksum; unsigned short i_id; unsigned short i_seq; // timestamp as a payload unsigned int timestamp; }; const int ICMP_LEN=32; // actual total length of IcmpHeader static SOCKET m_s=INVALID_SOCKET; // simple error handling for test program - just print a message and terminate static void FAIL(const char* msg) { printf("%s: %s\n",msg,strerror(errno)); exit(1); } static void init_socket(const char* szHost) { assert(szHost != NULL); // valid string needed assert(m_s == INVALID_SOCKET); // only call once, please m_s = socket(AF_INET,MY_SOCK_TYPE,IPPROTO_ICMP); if(m_s == INVALID_SOCKET) FAIL("socket()"); sockaddr_in dest; memset(&dest,0,sizeof(dest)); dest.sin_family = AF_INET; // convert host string from either x.x.x.x or hostname to ip address if((dest.sin_addr.s_addr = inet_addr(szHost)) == INADDR_NONE) { hostent* hp = gethostbyname(szHost); if(!hp) FAIL("Unknown host"); if(hp->h_addrtype != AF_INET) FAIL("Host is not an IP address"); assert(hp->h_length == sizeof(dest.sin_addr.s_addr)); memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length); } // quick message confirming hostname translated printf("connecting to %s (%s)\n",szHost,inet_ntoa(dest.sin_addr)); if(connect(m_s,(sockaddr*)&dest,sizeof(dest)) != 0) FAIL("connect()"); } static unsigned short checksum(const unsigned short* buf,int size) { unsigned int cksum=0; while(size >= sizeof(unsigned short)) { cksum += *buf++; size -= sizeof(unsigned short); } const unsigned char* bbuf = (unsigned char*)buf; while(size-- > 0) cksum += *buf++; cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return (unsigned short)(~cksum); } static void do_ping(void) { static unsigned short m_seq=100; // start non-zero just for grins char buf[1024]; // just a byte buffer for send/recv IcmpHeader* icmp = (IcmpHeader*)buf; icmp->i_type = ICMP_ECHO; icmp->i_code = 0; icmp->i_id = (unsigned short)getpid(); icmp->i_cksum = 0; icmp->i_seq = m_seq++; icmp->timestamp = clock(); // arbitrary thing to pass around memset(icmp+1,'E',ICMP_LEN-sizeof(IcmpHeader)); // fill with some junk icmp->i_cksum = checksum((const unsigned short*)icmp,ICMP_LEN); // // Send the ICMP request // size_t n = send(m_s,buf,ICMP_LEN,0); if(n == SOCKET_ERROR) FAIL("send()"); assert(n == ICMP_LEN); // // Wait for the ICMP response // fd_set fd; timeval tmo; FD_ZERO(&fd); FD_SET(m_s,&fd); tmo.tv_sec = 10; // timeout in secs tmo.tv_usec = 0; n = select(m_s,&fd,0,0,&tmo); if(n == SOCKET_ERROR) // error? FAIL("select()"); if(n == 0) { // timed out waiting // // THIS IS THE PROBLEM ON MACOS - NEVER GETS PAST HERE, ALWAYS TIMES OUT // printf("Timed out with no reply received\n"); return; } assert(n == 1 && FD_ISSET(m_s,&fd)); // // Receive the ICMP response // n = recv(m_s,buf,sizeof(buf),0); if(n == SOCKET_ERROR) // error? FAIL("recv()"); if(n < sizeof(IpHeader)) FAIL("Received invalid IP packet"); IpHeader* iphdr = (IpHeader*)buf; int h_len = (iphdr->h_len_version & 0x0F); // g++ bit packing hack if(n < (int)(h_len*4+sizeof(IcmpHeader))) FAIL("Received too few ICMP bytes in reply"); icmp = (IcmpHeader*)(buf + h_len*4); if(icmp->i_type != ICMP_ECHOREPLY) { printf("Received ICMP non-echo type %d\n",icmp->i_type); return; } if(icmp->i_id != (unsigned short)getpid()) printf("warning: received someone else's packet!\n"); if((icmp->i_seq+1) != m_seq) printf("warning: received reply out of sequence\n"); printf("Reply was received, %f secs\n",(double)(clock() - icmp->timestamp)/CLOCKS_PER_SEC); } static void close_socket(void) { if(m_s != INVALID_SOCKET) close(m_s); m_s = INVALID_SOCKET; } int main(int argc, char* argv[]) { #ifdef _WINDOWS WSADATA wsaData; if(WSAStartup(MAKEWORD(2,1),&wsaData) != 0) printf("WSAStartup failed: 0x%08x\n",::GetLastError()); #endif if(argc < 2) { printf("Usage: %s host\n",argv[0]); return 0; } int n = -1; init_socket(argv[1]); do_ping(); close_socket(); return 0; }
6d