Skip to content

Instantly share code, notes, and snippets.

@atoonk
Created March 25, 2024 00:36
Show Gist options
  • Save atoonk/61878dcda7bbbd25505f5edfffeca542 to your computer and use it in GitHub Desktop.
Save atoonk/61878dcda7bbbd25505f5edfffeca542 to your computer and use it in GitHub Desktop.
sending a packet with AF_PACKET in C
#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