Using SimplePing example to send ICMP with DF flag set

Hi,

I've tried to modify the simplePing example from here https://developer.apple.com/library/archive/samplecode/SimplePing/ and set the DF flag on.

In my attempt, I've used setsockopt right after socket was created :

fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);

int val = 1;
setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));

However, from wireshark I could clearly see that the icmp packet had the DF bit unset ... Please help me figure out what's wrong in my code.

Thanks !

val needs to be an CInt, not an Int. The latter is 64-bits on our platforms.

Did you check the error from setsockopt?

If you read back the option, what do you get back?

ps If you’re calling BSD Sockets from Swift see… well… Calling BSD Sockets from Swift.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

SimplePing is written in objective-C so I couldn't use Int/CInt instead I replaced int val to uint32_t val just to make sure I work with 32, and also made sure that the function setsockopt returns 0 which symbolize success.

However, when I trace the ping icmp packets in WireShark, I could clearly see that the DF bit is unset in the IP header.

In the SimplePing example, they first create underlying BSD socket, and than use it to create the core foundation. Here the relevant code :

    fd = -1;
    err = 0;
    switch (self.hostAddressFamily) {
        case AF_INET: {
            fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
            uint32_t val = 1;
            if (fd < 0) {
                err = errno;
                break;
            }
            int x = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));
            if (x < 0) {
                err = errno;
            }

        } break;

// after creating the BSD socket it create the CFSocket

        self.socket = (CFSocketRef) CFAutorelease( CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, SocketReadCallback, &context) );
        assert(self.socket != NULL);

        // The socket will now take care of cleaning up our file descriptor.
        assert( CFSocketGetSocketFlags(self.socket) & kCFSocketCloseOnInvalidate );
        fd = -1;
        
        rls = CFSocketCreateRunLoopSource(NULL, self.socket, 0);
        assert(rls != NULL);
        
        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
        
        CFRelease(rls);

I wonder if the DF option is being deleted somewhere when i create CFSocket from BSD socket.

SimplePing is written in objective-C

D’oh! I was reading your code through Swift colour glasses. Sorry about that.

I wonder if the DF option is being deleted somewhere when i create CFSocket from BSD socket.

No. CFSocket doesn’t mess with your data but, even if it did, SimplePing only uses CFSocket for run loop integration. All the sends and receives are done with BSD Sockets calls.

It’s possible that IP_DONTFRAG is simply not implemented with ICMP sockets. They are a weird be‍ast after all. On other platforms you tend to use a raw socket for ICMP, and raw sockets have IP_HDRINCL.

I’m not going to be able to answer this definitively in the time I have available on DevForums. If no one else chimes in, and you really need an answer, open a DTS tech support incident and we can pick things up in that context.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hey Quinn,

The trick with using raw socket worked indeed. Thanks !

I just had to create the socket in the following manner fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) and than I could use the setsockopt with the don't fragment flag.

one thing still puzzles me ... I've asked in the past whether there's a way to use the pmtu that is calculated in the OS level. I got a reply that it's already being calculated when using high level frameworks like the NSURLSession.. However, from looking at wireshark it seems that the pmtu is calculated all over again for every new tcp connection...

attached the wireshark traffic I saw for each https message I sent with NSURLSession based connection (you can see that it always starts with packet size as the mtu of current node, and after it gets rejections, it moved to the expected mtu which is 1000 - I deliberetly set the mtu of one of the hops on the way to this value)

So I wonder if in macOS there's a pmtu cache at all per route ? I used to think it's in the routing table that can be aquired using the following API :

    mib[0] = CTL_NET;
    mib[1] = PF_ROUTE;
    mib[2] = 0;
    mib[3] = 0;
    mib[4] = NET_RT_DUMP;
    mib[5] = 0;

sysctl(mib.data(), kSysctlMibLength, nullptr, &size_needed, nullptr, 0)
Using SimplePing example to send ICMP with DF flag set
 
 
Q