This article describes how to use pf_packet and sock_raw to send custom type Ethernet data packets, and use wireshare to capture packets to pave the way for adding network protocols to the Linux kernel.
First code:
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h> // close ()
# Include <string. h> // strcpy, memset (), and memcpy ()
# Include <netdb. h> // struct addrinfo
# Include <sys/types. h> // needed for socket (), uint8_t, uint16_t, uint32_t
# Include <sys/socket. h> // needed for socket ()
# Include <netinet/in. h> // ipproto_icmp, inet_addrstrlen
# Include <netinet/IP. h> // struct IP and ip_maxpacket (which is 65535)
# Include <netinet/ip_icmp.h> // struct ICMP, ICMP_Echo
# Include <ARPA/inet. h> // inet_ton () and inet_ntop ()
# Include <sys/IOCTL. h> // macro IOCTL is defined
# Include <bits/IOCTLs. h> // defines values for argument "request" of IOCTL.
# Include <net/If. h> // struct ifreq
# Include <Linux/if_ether.h> // eth_p_ip = 0x0800, eth_p_ipv6 = 0x86dd
# Include <Linux/if_packet.h> // struct sockaddr_ll (see man 7 packet)
# Include <net/Ethernet. h>
# Include <errno. h> // errno, perror ()
# Define eth_p_dean 0x8874 // custom Ethernet protocol type
Int main (INT argc, char ** argv)
{
Int I, datalen, frame_length, SD, bytes;
Char * interface = "eth1 ";;
Uint8_t data [ip_maxpacket];
Uint8_t src_mac [6];
Uint8_t dst_mac [6];
Uint8_t ether_frame [ip_maxpacket];
Struct sockaddr_ll device;
Struct ifreq IFR;
// Submit request for a socket descriptor to look up interface.
If (SD = socket (pf_packet, sock_raw, htons (eth_p_all) <0) {// The first time a socket is created to obtain information about the local Nic
Perror ("socket () failed to get socket descriptor for using IOCTL ()");
Exit (exit_failure );
}
// Use IOCTL () to look up interface name and get its MAC address.
Memset (& IFR, 0, sizeof (IFR ));
Snprintf (IFR. ifr_name, sizeof (IFR. ifr_name), "% s", interface );
If (IOCTL (SD, siocgifhwaddr, & IFR) <0 ){
Perror ("IOCTL () failed to get source MAC address ");
Return (exit_failure );
}
Close (SD );
// Copy source MAC address.
Memcpy (src_mac, IFR. ifr_hwaddr.sa_data, 6 );
// Report source MAC address to stdout.
Printf ("MAC address for interface % s is", interface );
For (I = 0; I <5; I ++ ){
Printf ("% 02x:", src_mac [I]);
}
Printf ("% 02x \ n", src_mac [5]);
// Find interface index from Interface Name and store index in
// Struct sockaddr_ll device, which will be used as an argument of sendto ().
Memset (& device, 0, sizeof (device ));
If (device. sll_ifindex = if_nametoindex (Interface) = 0 ){
Perror ("if_nametoindex () failed to obtain interface Index ");
Exit (exit_failure );
}
Printf ("index for interface % s is % I \ n", interface, device. sll_ifindex );
// Set destination MAC address: You need to fill these out
Dst_mac [0] = 0x10; // set the destination NIC address
Dst_mac [1] = 0x78;
Dst_mac [2] = 0xd2;
Dst_mac [3] = 0xc6;
Dst_mac [4] = 0x2f;
Dst_mac [5] = 0x89;
// Fill out sockaddr_ll.
Device. sll_family = af_packet;
Memcpy (device. sll_addr, src_mac, 6 );
Device. sll_halen = htons (6 );
// The length of the data to be sent can be arbitrary, but the minimum length of the data to be captured is 46. This is the minimum size of the Ethernet frame data domain to 46 bytes, which is not automatically zero-enable.
Datalen = 12;
Data [0] = 'H ';
Data [1] = 'E ';
Data [2] = 'l ';
Data [3] = 'l ';
Data [4] = 'O ';
Data [5] = '';
Data [6] = 'W ';
Data [7] = 'O ';
Data [8] = 'R ';
Data [9] = 'l ';
Data [10] = 'D ';
Data [11] = '! ';
// Fill out Ethernet frame header.
Frame_length = 6 + 6 + 2 + datalen;
// Destination and source MAC addresses
Memcpy (ether_frame, dst_mac, 6 );
Memcpy (ether_frame + 6, src_mac, 6 );
Ether_frame [12] = eth_p_dean/256;
Ether_frame [13] = eth_p_dean % 256;
// Data
Memcpy (ether_frame + 14, Data, datalen );
// Submit request for a raw socket descriptor.
If (SD = socket (pf_packet, sock_raw, htons (eth_p_all) <0) {// create a socket that is actually sent
Perror ("socket () failed ");
Exit (exit_failure );
}
// Send Ethernet frame to socket.
If (Bytes = sendto (SD, ether_frame, frame_length, 0, (struct sockaddr *) & device, sizeof (device) <= 0 ){
Perror ("sendto () failed ");
Exit (exit_failure );
}
Printf ("Send num = % d, read num = % d \ n", frame_length, bytes );
// Close socket descriptor.
Close (SD );
Return (exit_success );
}
Packet Capture:
In the next chapter, we use dev_add_pack in Linux to add the protocol test for this type.
Thanks to the reference code provided by David Buchan:
Http://www.pdbuchan.com/rawsock/rawsock.html