bdunagan

fill the void - bdunagan

31 Mar 2010
Multicast Ping on a Mac

While networks are awesome in the abstract, actual implementations are messy. Take multicast traffic. Network engineers can simply disable it, and I’m sure they have perfectly valid reasons for it. But as an uninformed user, what do you do if a multicast-enabled app doesn’t work? Is the failure the app’s fault, or is the network to blame? Enter Multicast Ping, available as an app download (PPC/Intel 10.4+) and an Xcode 3.2 project on GitHub.

Multicast Ping is a tool for testing multicast connectivity. It tests both directions, sending multicast packets to the supplied address/port while also listening for multicast packets on that same address/port. Run it on two Macs, and the app will let you know if the computers see each other. Both copies should display connections; if they don’t, the problem is in the network.

The icon is courtesy of Artua Design Studio via Iconspedia.

Multicast Code in Objective-C

There are a couple great tools for testing multicast connectivity: ssmping for Windows and Linux and mctester for python (which means most modern systems). However, I couldn’t find any equivalent implementations in Objective-C for the Mac, so I used mctester as a reference to write this Objective-C/Cocoa version.

The code (see below) is straight-forward: two methods for creating send and receive sockets and two methods for sending and receiving the multicast traffic. Take a look at the GitHub repo to see the code in context.

#pragma mark -
#pragma mark Send

- (void)createMulticastSendSocket {
	sendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	result = setsockopt(sendSocket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,(socklen_t)sizeof(ttl));
	result = setsockopt(sendSocket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,(socklen_t)sizeof(loop));
}

- (void)sendMulticast:(id)param {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	int seqno = 0;
	while (sendSocket != 0) {
		// Initialize address.
		struct sockaddr_in serverAddress;
		size_t namelen = sizeof(serverAddress);
		bzero(&serverAddress, namelen);
		serverAddress.sin_family = AF_INET;
		result = inet_aton([address cStringUsingEncoding:NSASCIIStringEncoding], &serverAddress.sin_addr);
		serverAddress.sin_port = htons(port);

		// Send packet.
		result = sendto(sendSocket, [message cStringUsingEncoding:NSASCIIStringEncoding], [message length], seqno, (struct sockaddr *)&serverAddress, namelen);
		seqno++;

		// Sleep for 1s.
		[NSThread sleepUntilDate:[[NSDate date] addTimeInterval:1]];
	}

	[pool drain];
}

#pragma mark -
#pragma mark Receive

- (void)createMulticastReceiveSocket {
	int flag = 1;

	receiveSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	struct sockaddr_in serverAddress;
	size_t namelen = sizeof(serverAddress);
	bzero(&serverAddress, namelen);
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddress.sin_port = htons(port);
	result = setsockopt(receiveSocket, SOL_SOCKET, SO_REUSEADDR, &flag,(socklen_t)sizeof(flag));
	result = bind(receiveSocket, (struct sockaddr *)&serverAddress, (socklen_t)namelen);
	if (result != 0) {
		// Couldn't bind. Port probably in use. Let the parent process know then terminate.
		[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:[@"port in use" dataUsingEncoding:NSUTF8StringEncoding]];
		exit(1);
	}

	struct ip_mreq  theMulti;
	result = inet_aton([address cStringUsingEncoding:NSASCIIStringEncoding], &theMulti.imr_multiaddr );
	result = inet_aton([LOCAL_ADDRESS cStringUsingEncoding:NSASCIIStringEncoding], &theMulti.imr_interface );
	result = setsockopt(receiveSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &theMulti,(socklen_t)sizeof(theMulti));
}

- (void)receiveMulticast:(id)param {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	char receivedLine[1000];
	while (receiveSocket != 0) {
		bzero(&receivedLine, 1000);
		struct sockaddr_in receiveAddress;
		socklen_t receiveAddressLen = sizeof(receiveAddress);

		// Receive packet.
		result = recvfrom(receiveSocket, receivedLine, 1000, 0, (struct sockaddr *)&receiveAddress, &receiveAddressLen);
		if(result > 0) {
			// Extract address, port, message.
			UInt16 sentPort = ntohs(receiveAddress.sin_port);
			char addressBuffer[INET_ADDRSTRLEN];
			inet_ntop(AF_INET, &receiveAddress.sin_addr, addressBuffer, sizeof(addressBuffer));
			NSString *sentHost = [NSString stringWithCString:addressBuffer encoding:NSASCIIStringEncoding];
			NSString *receivedMessage = [NSString stringWithCString:receivedLine encoding:NSASCIIStringEncoding];

			// Write out the new message to the pipe.
			NSString *line = [NSString stringWithFormat:@"%@,%d,%@", sentHost, sentPort, receivedMessage];
			[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
		}
		else {
			NSLog(@"result %d", result);
		}

		// Sleep for 1s.
		[NSThread sleepUntilDate:[[NSDate date] addTimeInterval:1]];
	}

	[pool drain];
}

Again, the app is available for download (PPC/Intel 10.4+).

Previous LinkedIn Twitter GitHub Email Next