작성일: 2023년 10월 10일
패킷 보내기 (Send a packet)
예제 코드
/** * How to build * $ gcc send-raw-packet.c -o send-raw-packet * * How to run * $ ./send-raw-packet * or * $ ./send-raw-packet eth0 */ #include <arpa/inet.h> #include <linux/if_packet.h> #include <linux/ip.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 <errno.h> // FIXME: MY_DEST_MACX 값은 각자 테스트 환경이 맞게 수정해서 사용. #define MY_DEST_MAC0 0x52 #define MY_DEST_MAC1 0x54 #define MY_DEST_MAC2 0x00 #define MY_DEST_MAC3 0xcf #define MY_DEST_MAC4 0xab #define MY_DEST_MAC5 0x76 // FIXME: DEFAULT_IF 값은 각자 테스트 환경이 맞게 수정해서 사용. #define DEFAULT_IF "enp7s0" #define BUF_SIZ 1024 int main(int argc, char *argv[]) { int sockfd; int tx_len = 0; char sendbuf[BUF_SIZ]; char ifName[IFNAMSIZ]; struct ifreq if_idx; struct ifreq if_mac; struct ether_header *eh = (struct ether_header *) sendbuf; struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header)); struct sockaddr_ll socket_address; // Network interface name 지정하기 (예: eth0) if (argc > 1) { strcpy(ifName, argv[1]); } else { strcpy(ifName, DEFAULT_IF); } // RAW socket 사용을 위한 File descriptor 생성하기 if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) { printf("socket() error: %d (%s)\n", errno, strerror(errno)); return 0; } // Network interface의 index 값 구하기 memset(&if_idx, 0, sizeof(struct ifreq)); strncpy(if_idx.ifr_name, ifName, IFNAMSIZ-1); if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) { printf("ioctl(SIOCGIFINDEX, %s) error: %d (%s)\n", ifName, errno, strerror(errno)); return 0; } // Network interface의 MAC address 구하기 memset(&if_mac, 0, sizeof(struct ifreq)); strncpy(if_mac.ifr_name, ifName, IFNAMSIZ-1); if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) { printf("ioctl(SIOCGIFHWADDR, %s) error: %d (%s)\n", ifName, errno, strerror(errno)); return 0; } // Ehternet header 구성하기 (참고: sendbuf pointer가 eh 주소를 pointing) memset(sendbuf, 0, BUF_SIZ); /* * ioctl() 함수를 이용해서 얻은 'enp7s0' NIC에 대한 MAC Address 값을 * ethernet header 구조체의 ether_shost 변수에 복사한다. */ printf("( %s ) MAC address = ", ifName); for (int idx = 0; idx < 6; idx++) { eh->ether_shost[idx] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[idx]; printf("%02x", eh->ether_shost[idx]); if (idx < 5) { printf(":"); } } printf("\n"); /* * 일반적으로 NIC port의 MAC address를 ethernet frame의 source address로 사용하지만 * Ethernet packet 전송 테스트를 위해서 가짜 Source MAC address를 만들었다. */ eh->ether_shost[3] = 0x01; eh->ether_shost[4] = 0x02; eh->ether_shost[5] = 0x03; // Ethernet frame - Destination host MAC address eh->ether_dhost[0] = MY_DEST_MAC0; eh->ether_dhost[1] = MY_DEST_MAC1; eh->ether_dhost[2] = MY_DEST_MAC2; eh->ether_dhost[3] = MY_DEST_MAC3; eh->ether_dhost[4] = MY_DEST_MAC4; eh->ether_dhost[5] = MY_DEST_MAC5; // Ethertype: Internet Protocol (0x0800) eh->ether_type = htons(ETH_P_IP); tx_len += sizeof(struct ether_header); // FIXME: IP Header // 각자 테스트 환경에 맞게 iph 변수를 수정하여 사용하기 iph->ihl = 20 >> 2; // NOTE: (20 >> 2) * 4 = 20 bytes (IHL은 4 byte 단위로 해석되기 때문) iph->version = 4; iph->protocol = IPPROTO_IP; iph->saddr = inet_addr("10.1.1.10"); iph->daddr = inet_addr("10.1.1.11"); iph->tot_len = 46 + 32; tx_len += sizeof(struct iphdr); /* Payload (Packet data) */ for (char idx = 0; idx < 32; idx++) { sendbuf[tx_len++] = idx; } /* Index of the network device */ socket_address.sll_ifindex = if_idx.ifr_ifindex; /* Address length*/ socket_address.sll_halen = ETH_ALEN; /* Destination MAC */ socket_address.sll_addr[0] = MY_DEST_MAC0; socket_address.sll_addr[1] = MY_DEST_MAC1; socket_address.sll_addr[2] = MY_DEST_MAC2; socket_address.sll_addr[3] = MY_DEST_MAC3; socket_address.sll_addr[4] = MY_DEST_MAC4; socket_address.sll_addr[5] = MY_DEST_MAC5; /* Send packet */ if (sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0) printf("Send failed\n"); return 0; }
패킷 받기 (Receive a packet)
예제 코드
/** * How to build * $ gcc recv-raw-packet.c -o recv-raw-packet * * How to run * $ ./recv-raw-packet * or * $ ./recv-raw-packet eth0 */ #include <unistd.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <linux/ip.h> #include <linux/udp.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 <errno.h> #define ETHER_TYPE 0x0800 #define DEFAULT_IF "enp7s0" #define BUF_SIZ 1024 int main(int argc, char *argv[]) { char sender[INET6_ADDRSTRLEN]; int sockfd, ret; int sockopt; ssize_t pktbytes; struct ifreq ifopts; // To set promiscuous mode struct ifreq if_ip; // To get IP address of this host NIC struct sockaddr_storage peer_addr; uint8_t buf[BUF_SIZ]; char ifName[IFNAMSIZ]; // Network interface name 지정하기 (예: eth0) if (argc > 1) { strcpy(ifName, argv[1]); } else { strcpy(ifName, DEFAULT_IF); } // Ethernet + IP + UDP header struct ether_header *eh = (struct ether_header *) buf; struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header)); struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct iphdr) + sizeof(struct ether_header)); if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1) { printf("socket(PF_PACKET, SOCK_RAW, ETHER_TYPE) error: %d (%s)\n", errno, strerror(errno)); return -1; } // Set interface to promiscuous mode strncpy(ifopts.ifr_name, ifName, IFNAMSIZ-1); ioctl(sockfd, SIOCGIFFLAGS, &ifopts); ifopts.ifr_flags |= IFF_PROMISC; ioctl(sockfd, SIOCSIFFLAGS, &ifopts); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) { printf("setsockopt(SO_REUSEADDR) error: %d (%s)\n", errno, strerror(errno)); close(sockfd); exit(0); } // Bind to device if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ-1) == -1) { printf("setsockopt(SO_BINDTODEVICE, %s) error: %d (%s)\n", ifName, errno, strerror(errno)); close(sockfd); exit(0); } repeat: printf("\nWaiting to recvfrom...\n"); pktbytes = recvfrom(sockfd, buf, BUF_SIZ, 0, NULL, NULL); printf(" Got packet %lu bytes\n", pktbytes); printf("Destination MAC address = "); for (int idx = 0; idx < 6; idx++) { printf("%02x", eh->ether_dhost[idx]); if (idx < 5) { printf(":"); } } printf("\n"); // Get source IP ((struct sockaddr_in *)&peer_addr)->sin_addr.s_addr = iph->saddr; inet_ntop(AF_INET, &((struct sockaddr_in*)&peer_addr)->sin_addr, sender, sizeof sender); // Look up my device IP addr memset(&if_ip, 0, sizeof(struct ifreq)); strncpy(if_ip.ifr_name, ifName, IFNAMSIZ-1); if (ioctl(sockfd, SIOCGIFADDR, &if_ip) >= 0) { printf("Source IP: %s\n My IP: %s\n", sender, inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr)); // Ignore if I sent it if (strcmp(sender, inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr)) == 0) { printf("but I sent it :(\n"); ret = -1; goto done; } } /* UDP payload length */ ret = ntohs(udph->len) - sizeof(struct udphdr); /* Print packet */ printf("\tData:"); for (int idx = 0; idx < pktbytes; idx++) { printf("%02x ", buf[idx]); } printf("\n"); done: goto repeat; close(sockfd); return ret; }
'Network' 카테고리의 다른 글
Juniper Switch 제어를 위한 Python Script 예제 (Junos PyEX 패키지 사용하기) (0) | 2023.12.11 |
---|---|
CISCO TRex - Network Traffic Generator (네트워크 성능 계측기), 설치 및 테스트 (0) | 2023.11.12 |
IP 패킷 조작 툴 - Scapy (0) | 2023.09.25 |
PVLAN (Private VLAN) 개념 (스터디 자료 모음) (0) | 2023.09.23 |
netconf 사용 예제 코드(CISCO ISO, Juniper JunOS, Arista EOS 등) (0) | 2023.09.19 |