Created
March 25, 2024 00:36
-
-
Save atoonk/61878dcda7bbbd25505f5edfffeca542 to your computer and use it in GitHub Desktop.
sending a packet with AF_PACKET in C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <arpa/inet.h> | |
#include <linux/if_packet.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <sys/ioctl.h> | |
#include <sys/socket.h> | |
#include <net/if.h> | |
#include <netinet/ether.h> | |
#include <netinet/ip.h> // Include for struct iphdr | |
#include <netinet/ip_icmp.h> // Include for struct icmphdr and ICMP_ECHO | |
#define DEFAULT_IF "veth0" | |
#define BUF_SIZ 1024 | |
// Convert MAC address string to binary form | |
void mac_addr_a2n(uint8_t * mac_addr, char * str) { | |
int i; | |
for (i = 0; i < 6; i++) { | |
unsigned int val; | |
sscanf(str + 3 * i, "%2x", & val); | |
mac_addr[i] = (uint8_t) val; | |
} | |
} | |
// Simplified checksum calculation for this example | |
unsigned short checksum(void * b, int len) { | |
unsigned short * buf = b; | |
unsigned int sum = 0; | |
unsigned short result; | |
for (sum = 0; len > 1; len -= 2) | |
sum += * buf++; | |
if (len == 1) | |
sum += * (unsigned char * ) buf; | |
sum = (sum >> 16) + (sum & 0xFFFF); | |
sum += (sum >> 16); | |
result = ~sum; | |
return result; | |
} | |
// Constructs a network packet, including Ethernet, IP, and ICMP headers, | |
// and prepares it for sending through a raw socket. | |
void buildPacket(int sockfd, char * sendbuf, struct sockaddr_ll * socket_address, char * ifName, int * tx_len) { | |
// Cast the beginning of sendbuf to an Ethernet header structure | |
struct ether_header * eh = (struct ether_header * ) sendbuf; | |
// Calculate the starting address of the IP header just after the Ethernet header | |
struct iphdr * iph = (struct iphdr * )(sendbuf + sizeof(struct ether_header)); | |
// Calculate the starting address of the ICMP header just after the IP header | |
struct icmphdr * icmph = (struct icmphdr * )(sendbuf + sizeof(struct ether_header) + sizeof(struct iphdr)); | |
// Convert and set source MAC address from string | |
mac_addr_a2n(eh -> ether_shost, "16:f5:fc:be:ad:79"); | |
// Convert and set destination MAC address from string | |
mac_addr_a2n(eh -> ether_dhost, "1a:ac:5b:58:16:91"); | |
// Set the Ethernet frame type to IPv4 (0x0800) | |
eh -> ether_type = htons(ETH_P_IP); | |
// Update the total length of the packet to include the Ethernet header | |
* tx_len += sizeof(struct ether_header); | |
// Configure the IP header | |
iph -> ihl = 5; // Header length (5 means 20 bytes as each unit is 4 bytes) | |
iph -> version = 4; // IPv4 | |
iph -> tos = 0; // Type of service - routine precedence | |
iph -> tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) + 56); // Total length of IP packet | |
iph -> id = htons(54321); // Packet ID, arbitrary number | |
iph -> frag_off = 0; // Fragment offset for packet segmentation | |
iph -> ttl = 64; // Time to live, determines the packet's lifetime | |
iph -> protocol = IPPROTO_ICMP; // The protocol used is ICMP | |
iph -> check = 0; // Initially zero; checksum calculated next | |
iph -> saddr = inet_addr("192.168.64.1"); // Source IP address | |
iph -> daddr = inet_addr("192.168.64.2"); // Destination IP address | |
// Calculate and set the IP header checksum | |
iph -> check = checksum(iph, sizeof(struct iphdr)); | |
// Update total length of the packet to include the IP header | |
* tx_len += sizeof(struct iphdr); | |
// Configure the ICMP header for an echo request | |
icmph -> type = ICMP_ECHO; // Echo request type | |
icmph -> code = 0; // No code for echo request | |
icmph -> un.echo.id = htons(1); // Echo request ID, arbitrary number | |
icmph -> un.echo.sequence = htons(1); // Sequence number, arbitrary number | |
icmph -> checksum = 0; // Initially zero; checksum calculated next | |
// Calculate and set the ICMP checksum, accounting for any payload | |
icmph -> checksum = checksum(icmph, sizeof(struct icmphdr) + 56); | |
// Update total length of the packet to include the ICMP header and payload | |
* tx_len += sizeof(struct icmphdr) + 56; | |
// Configure the sockaddr_ll structure for the outgoing packet (link_layer) | |
// This includes setting protocol, ifindex, and the MAC address | |
memset(socket_address, 0, sizeof(struct sockaddr_ll)); | |
socket_address -> sll_family = AF_PACKET; // Always AF_PACKET for raw sockets | |
socket_address -> sll_protocol = htons(ETH_P_IP); // Ethernet protocol | |
socket_address -> sll_ifindex = if_nametoindex(ifName); // Interface index | |
if (socket_address -> sll_ifindex == 0) { | |
perror("if_nametoindex failed"); // Error handling for invalid interface | |
exit(EXIT_FAILURE); | |
} | |
// also set sll_pkttype | |
socket_address -> sll_halen = ETH_ALEN; // Address length | |
memcpy(socket_address -> sll_addr, eh -> ether_dhost, ETH_ALEN); // Copy destination MAC address | |
} | |
int main(int argc, char * argv[]) { | |
// Declare a socket file descriptor for communication. | |
int sockfd; | |
// Buffer to hold the outgoing packet. | |
char sendbuf[BUF_SIZ]; | |
// sockaddr_ll structure to specify the interface and other parameters for sending. | |
struct sockaddr_ll socket_address; | |
// Buffer to hold the name of the interface to use. | |
char ifName[IFNAMSIZ]; | |
// Variable to keep track of the total length of the packet to send. | |
int tx_len = 0; | |
// Determine which network interface to use for sending the packet. | |
// If an interface name is passed as a command-line argument, use it. | |
// Otherwise, use the default interface name. | |
if (argc > 1) | |
strcpy(ifName, argv[1]); | |
else | |
strcpy(ifName, DEFAULT_IF); | |
// Open a RAW socket to send packets at the Ethernet level. | |
// SOCK_RAW sockets allow sending of raw packets to the network. | |
// IPPROTO_RAW implies IP headers are included in the packet. | |
if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) { | |
// If socket creation fails, print an error message and exit. | |
perror("socket"); | |
} | |
// Build the packet to be sent. | |
// This function prepares the Ethernet, IP, and ICMP headers, | |
// and fills in the sockaddr_ll structure with the appropriate values | |
// for the selected interface. | |
buildPacket(sockfd, sendbuf, & socket_address, ifName, & tx_len); | |
// Send the prepared packet. | |
// sendto() is used with RAW sockets to send packets. | |
// The socket_address structure provides the destination interface. | |
if (sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr * ) & socket_address, sizeof(struct sockaddr_ll)) < 0) { | |
// If packet sending fails, print an error message and return 1. | |
perror("Send failed"); | |
return 1; | |
} | |
// Successful execution path. | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment