Nicolas Electronics

Electronic projects and programming

Ping Google DNS with your PSVita

As I said in a previous post, I’ve contributed to some samples for the VITASDK.

This one is interesting to me as I’ve never used sockets before.
This sample is going to ping the Google DNS server 8.8.8.8.

I have to admit that I scratched my head for a while when trying to ping my laptop and not getting a response even though I saw the ping request in Wireshark.
An IRC (#henkaku & #vitasdk) user by the name of xyz told me to try to ping 8.8.8.8 and that the firewall in my laptop might be blocking the PSVita’s ping request.

He was right! I pinged 8.8.8.8 and I immediately got a reply… Stupid laptop…

You can find the sample here : https://github.com/vitasdk/samples/tree/master/socket_ping

The ping request uses ICMP (Internet Control Message Protocol), so I’ve made a structure for this protocol.
The ICMP consists of a ICMP header and a payload. You can find the header information on this wikipedia page.

1
2
3
4
5
6
7
8
/* Arbitrary payload size for ICMP */
#define ICMP_MIN_PAYLOAD (32)
 
/* ICMP Packet structure (= icmp header + payload) */
typedef struct{
SceNetIcmpHeader hdr;
char payload[ICMP_MIN_PAYLOAD];
}IcmpPacket;

Note that this structure uses an SceNetIcmpHeader structure, defined in the network include file of the PSVita.
There’s no predefined payload size for an ICMP packet, so I’ve just chosen to use a payload of 32 bytes.

The reply we get from 8.8.8.8 is a TCP packet containing an ICMP reply packet, so I’ve made a structure for that too:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* TCP Flag bits struct */
typedef struct{
	struct{
		unsigned short : 7; /* data offset + reserved, not used in this struct */
		unsigned short ns : 1;
		unsigned short cwr : 1;
		unsigned short ece : 1;
		unsigned short urg : 1;
		unsigned short ack : 1;
		unsigned short psh : 1;
		unsigned short rst : 1;
		unsigned short syn : 1;
		unsigned short fin : 1;
	};
}TcpFlagBits;
 
/* From wikipedia TCP Header table */
/* TCP header struct */
typedef struct{
	uint16_t src;
	uint16_t dst;
	uint32_t sequence;
	uint32_t ack_num;
	union{
		uint16_t data_flags;
		struct{
			uint16_t data_offset : 4;
			uint16_t reserved : 3;
			uint16_t flags : 9;
		};
		TcpFlagBits bit;
	};
	uint16_t window_size;
	uint16_t checksum;
	uint16_t urgent_ptr;
}TcpHdr;
 
/* TCP Packet structure (= tcp header + icmp packet) */
typedef struct{
	TcpHdr hdr;
	IcmpPacket icmp;
}TcpPacket;

I used Wikipedia to make make the TCP header structure and added an IcmpPacket to the TcpPacket structure.
I figured out what data I received from 8.8.8.8 by printing out those bytes and matching them to the ICMP and TCP header.

Now that I have these two structures, I need a way to access the data as a char* and as a uint16_t*.
Accessing this data as a char* is necessary to send the data out and accessing the data through a uint16_t* is necessary to calculate the checksum in the ICMP header.
This is why I made two union structures:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 
	Union needed to correctly access the network packets
	- IcmpPacket for the structure of the packet 
	- uin16_t[] for the checksum
	- char[] for the sendto and recvfrom functions
*/
typedef union{
	IcmpPacket icmp_struct;
	uint16_t icmp_u16buff[sizeof(IcmpPacket)/sizeof(uint16_t)];
	char icmp_packet[sizeof(IcmpPacket)];
}IcmpUnion;
 
typedef union{
	TcpPacket tcp_struct;
	uint16_t tcp_u16buff[sizeof(TcpPacket)/sizeof(uint16_t)];
	char tcp_packet[sizeof(TcpPacket)];
}TcpUnion;

Next thing to do is create a socket like so :

1
sfd = sceNetSocket("ping_test", SCE_NET_AF_INET, SCE_NET_SOCK_RAW, SCE_NET_IPPROTO_ICMP);

Allow the socket to broadcast, so change the socket option:

1
retval = sceNetSetsockopt(sfd, SCE_NET_SOL_SOCKET, SCE_NET_SO_BROADCAST, (const void*)&on, sizeof(on));

Now it’s time to fill the ICMP packet:

1
2
3
4
5
6
7
8
icmp.icmp_struct.hdr.type = SCE_NET_ICMP_TYPE_ECHO_REQUEST; /* set icmp type to echo request */
icmp.icmp_struct.hdr.code = SCE_NET_ICMP_CODE_DEST_UNREACH_NET_UNREACH; /* set code to 0 */
icmp.icmp_struct.hdr.un.echo.id = 0x1; /* arbitrary id */
icmp.icmp_struct.hdr.un.echo.sequence = 0x1234; /* arbitrary sequence */
 
/* fill payload with random text, this will get sent back */
strncpy(icmp.icmp_struct.payload, "Random Payload in ping", ICMP_MIN_PAYLOAD); /* NB: strncpy fills the remainder of the buffer with zeroes */
icmp.icmp_struct.hdr.checksum = in_cksum(icmp.icmp_u16buff, sizeof(IcmpPacket)); /* compute checksum */

Once sent, receive data like so :

1
received_data = sceNetRecvfrom(sfd, tcp.tcp_packet, sizeof(TcpPacket), SCE_NET_MSG_WAITALL, &from_addr, (unsigned int*)&from_len);

And voilĂ , that’s basically it.

The sample displays the bytes of the network packets and adds a bit of background color, just so you can see what the bytes are.
Here’s a screenshot of the sample :

Leave a Reply

Your email address will not be published. Required fields are marked *

Please type the characters of this captcha image in the input box

Please type the characters of this captcha image in the input box