libpcap 使用範例
最近因為工作上的需求,所以透過 libpcap 寫了一個小程式。程式紀錄在下面。應該不難懂,所以就不多做說明。程式主要參考sniffex,不過主要針對 DHCP 的封包進行攔截以及顯示。
#include不過討厭的地方在於,雖然 libpcap 有提供#include #include #include #include #include #include #include #define SIZE_ETHERNET (14) #define SIZE_UDP (8) #define ETHER_ADDR_LEN (6) const char *DHCP_MSG_TYPE_STR[9] = { "None", "DHCP Discover", "DHCP Offer", "DHCP Request", "DHCP Decline", "DHCP ACK", "DHCP NACK", "DHCP Release", "DHCP Inform" }; struct sniff_ethernet { uint8_t ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ uint8_t ether_shost[ETHER_ADDR_LEN]; /* source host address */ uint16_t ether_type; /* IP? ARP? RARP? etc */ }; struct sniff_ip { uint8_t ip_vhl; /* version << 4 | header length >> 2 */ uint8_t ip_tos; /* type of service */ uint16_t ip_len; /* total length */ uint16_t ip_id; /* identification */ uint16_t ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ uint8_t ip_ttl; /* time to live */ uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; #define IP_HL(ip) ( ( ( ip ) -> ip_vhl ) & 0x0f ) #define IP_V(ip) ( ( ( ip ) -> ip_vhl ) >> 4 ) struct sniff_udp { uint16_t udp_sport; /* source port */ uint16_t udp_dport; /* destination port */ uint16_t udp_len; /* source port */ uint16_t udp_sum; /* source port */ }; struct sniff_dhcp { uint8_t dhcp_op; uint8_t dhcp_htype; // 1: ethernet uint8_t dhcp_hlen; // 6: ethernet address length uint8_t dhcp_hops; uint32_t dhcp_xid; uint16_t dhcp_secs; uint16_t dhcp_flags; struct in_addr dhcp_ciaddr; // Client IP address struct in_addr dhcp_yiaddr; // Your IP address struct in_addr dhcp_siaddr; // Server IP address struct in_addr dhcp_giaddr; // GW IP address uint8_t dhcp_chaddr[16]; // Client HW address uint8_t dhcp_legacy[192]; // For legacy bootps compatibility uint32_t dhcp_magic_cookie; // Magic Cookie }; void parse_dhcp_packet( u_char *args, const struct pcap_pkthdr *header, const u_char *packet ) { static int count = 1; const struct sniff_ethernet *eth_hdr; const struct sniff_ip *ip_hdr; const struct sniff_udp *udp_hdr; const struct sniff_dhcp *dhcp_hdr; const char *payload; int size_ip = 0; int size_dhcp_options = 0; uint8_t *dhcp_options = NULL; uint8_t dhcp_option_code = 0; uint8_t dhcp_option_len = 0; char hostname[260]; // 256 + 4 printf( "Packet number: %d\n", count ); eth_hdr = ( struct sniff_ethernet * ) packet; ip_hdr = ( struct sniff_ip * )( packet + SIZE_ETHERNET ); size_ip = IP_HL( ip_hdr ) * 4; printf( "\tIP Header length: %d bytes\n", size_ip ); printf( "\tIP Src: %s\n", inet_ntoa( ip_hdr -> ip_src ) ); printf( "\tIP Dst: %s\n", inet_ntoa( ip_hdr -> ip_dst ) ); switch( ip_hdr -> ip_p ) { case IPPROTO_TCP: printf( "\tProtocol: TCP\n" ); break; case IPPROTO_UDP: printf( "\tProtocol: UDP\n" ); break; case IPPROTO_ICMP: printf( "\tProtocol: ICMP\n" ); break; default: printf( "\tProtocol: Others\n" ); break; } if( ip_hdr -> ip_p != IPPROTO_UDP ) { printf( "\t==> Only handle DHCP\n" ); return; } udp_hdr = ( struct sniff_udp * )( packet + SIZE_ETHERNET + size_ip ); printf( "\tSrc port: %d\n", ntohs( udp_hdr -> udp_sport ) ); printf( "\tDst port: %d\n", ntohs( udp_hdr -> udp_dport ) ); printf( "\tlength: %d\n", ntohs( udp_hdr -> udp_len ) ); dhcp_hdr = ( struct sniff_dhcp * )( packet + SIZE_ETHERNET + size_ip + SIZE_UDP ); printf( "\tDHCP OP: %02X\n", dhcp_hdr -> dhcp_op ); printf( "\tDHCP HTYPE: %02X\n", dhcp_hdr -> dhcp_htype ); printf( "\tDHCP HLEN: %02X\n", dhcp_hdr -> dhcp_hlen ); printf( "\tDHCP HOPS: %02X\n", dhcp_hdr -> dhcp_hops ); printf( "\tDHCP XID: %08X\n", dhcp_hdr -> dhcp_xid ); printf( "\tDHCP SECS: %04X\n", dhcp_hdr -> dhcp_secs ); printf( "\tDHCP FLAGS: %04X\n", dhcp_hdr -> dhcp_flags ); printf( "\tDHCP CIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_ciaddr ) ); printf( "\tDHCP YIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_yiaddr ) ); printf( "\tDHCP SIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_siaddr ) ); printf( "\tDHCP GIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_giaddr ) ); printf( "\tDHCP CHADDR: %02X:%02X:%02X:%02X:%02X:%02X\n", dhcp_hdr -> dhcp_chaddr[0], dhcp_hdr -> dhcp_chaddr[1], dhcp_hdr -> dhcp_chaddr[2], dhcp_hdr -> dhcp_chaddr[3], dhcp_hdr -> dhcp_chaddr[4], dhcp_hdr -> dhcp_chaddr[5] ); printf( "\tDHCP Magic Cookie: %08X\n", dhcp_hdr -> dhcp_magic_cookie ); size_dhcp_options = ntohs( udp_hdr -> udp_len ) - sizeof( struct sniff_dhcp ); dhcp_options = ( uint8_t * )( packet + SIZE_ETHERNET + size_ip + SIZE_UDP + sizeof( struct sniff_dhcp ) ); while( size_dhcp_options > 0 ) { dhcp_option_code = dhcp_options[0]; if( dhcp_option_code == 0 ) // Padding, no length { size_dhcp_options--; dhcp_options++; continue; } dhcp_option_len = dhcp_options[1]; printf( "\t\tDHCP Option (%d)", dhcp_option_code ); switch( dhcp_option_code ) { case 53: // DHCP Message Type printf( ": DHCP Message Type\n" ); printf( "\t\t\tLength: %d\n", dhcp_option_len ); printf( "\t\t\tType: %s\n", DHCP_MSG_TYPE_STR[ dhcp_options[2] ] ); break; case 50: // Requested IP address printf( ": Requested IP Address\n" ); printf( "\t\t\tLength: %d\n", dhcp_option_len ); printf( "\t\t\tIP: %s\n", inet_ntoa( *( struct in_addr * )( dhcp_options + 2 ) ) ); break; case 12: // Host name printf( ": Host Name\n" ); printf( "\t\t\tLength: %d\n", dhcp_option_len ); memset( hostname, 0, 260 ); memcpy( hostname, dhcp_options + 2, dhcp_option_len ); printf( "\t\t\tHost Name: %s\n", hostname ); break; case 255: // End printf( "\n\t\t\tLength: 0\n" ); dhcp_option_len = -1; break; default: printf( "\n\t\t\tLength: %d\n", dhcp_option_len ); break; } dhcp_options = dhcp_options + dhcp_option_len + 2; size_dhcp_options = size_dhcp_options - ( dhcp_option_len + 2 ); } count++; return; } int main( int argc, char **argv ) { char *dev = "eth0"; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle = NULL; char filter_exp[] = "port bootps"; struct bpf_program fp; bpf_u_int32 mask; bpf_u_int32 net; printf( "%s: this process is a DHCP sniffer example.\n", argv[0] ); if( argc == 1 ) { // eth0 } else if( argc == 2 ) { dev = argv[1]; } else { printf( "Usage:\n" ); printf( "%s [iface]\n", argv[0] ); printf( "ps: If no interface is given, default interface is eth0.\n" ); } if( pcap_lookupnet( dev, &net, &mask, errbuf ) == -1 ) { printf( "Cannot get IP and netmask for device %s.\n", dev ); net = 0; mask = 0; } // Open the capture device. // 1: promiscuous mode // -1: never timeout handle = pcap_open_live( dev, 1518, 1, 1000, errbuf ); if( handle == NULL ) { printf( "Cannot open device %s.\n", dev ); return -1; } // Compile the filter if( pcap_compile( handle, &fp, filter_exp, 0, net ) == -1 ) { printf( "Cannot parse the filter: \n%s --> %s\n", filter_exp, pcap_geterr( handle ) ); return -1; } // Apply filter if( pcap_setfilter( handle, &fp ) == -1 ) { printf( "Cannot install the filter: \n%s --> %s\n", filter_exp, pcap_geterr( handle ) ); return -1; } // Start capture pcap_loop( handle, -1, parse_dhcp_packet, NULL ); return 0; }
pcap_get_selectable_fd ,但跟 select 或是 epoll 似乎不太容易結合使用,詳情請洽 manual。
作者已經移除這則留言。
回覆刪除作者已經移除這則留言。
回覆刪除Hi, 我是哲緯, 好久不見!
回覆刪除剛好在查 libcap 看到你這篇文章,好奇問一下你的 BPF Filter 設定的 expression
是不是只撈單一方向的 DHCP packets?
因為 char filter_exp[] = "port bootps";
我查了一下 "port bootps" 可以撈到 DHCPOFFER, DHCPACK 但是 DHCPDISCOVER, DHCPREQUEST 好像會被過濾掉,除非 Filter 下 "port bootps or port bootpc"
thanks
Hello 好久不見,最近還好嗎?
刪除我剛剛實驗了一下,Request 和 ACK 都可以撈到。
當初這樣寫的原因在於 DHCP 所用的 port 是 67(bootps) 和 68 (bootpc)
我在 filter_exp 那邊並沒有指定 src port 或是 dst port
所以只要有一端的 port 滿足應該就可以了
Blogger的留言好像系統怪怪的,我最近在教育部服替代役
刪除我想我大概瞭解了,因為 port bootps 的 filter expression 並沒有限制是source port或dest. port 所以兩個方向的封包,不管是以src=67 或是 dst=67都會撈到!
不過如果filter expression 下 "bootps" 應該會比較好