#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> // for TCP protocol in transport layer
#include <arpa/inet.h> // include for inet_ntoa()
#include <netinet/in.h> // defined TCP Procotol Type information
/* TCP 및 IP Protocol 에 관련된 정보 포함.
대표적으로 well-known port number 및 IP 체계 */
#include <pcap.h> // pcap func used errbuf[], errbuf size = 256
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#define PCAP_CNT_MAX 1
#define PCAP_SNAPSHOT 1024
#define PCAP_TIMEOUT 100
#define FILTER_RULE "host 192.168.100.100" // Filtering taget IP and Port Number
void packet_view(unsigned char* user, const struct pcap_pkthdr *h, const unsigned char *p);
int main(int argc, char* argv[]) {
char* dev;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net;
bpf_u_int32 mask; // struct in_addr은 <netinet/in.h>에 정의.
struct in_addr net_addr, mask_addr;
pcap_t *pd; // struct bpf_program은 <pcap-bpf.h>에 정의.
struct bpf_program fcode;
/* struct bpf_program {
u_int bf_len;
struct bpf_insn *bf_insns;
} */
/* pcap_lookupdev is find network interface. if finding network interface, it return that found network interface name.
pcap_lookupdev : fail => return null / success => char* pointer (NIC name) */
if(!(dev = pcap_lookupdev(errbuf))) {
perror(errbuf);
exit(1);
}
if(pcap_lookupnet(dev, &net, &mask, errbuf) < 0) {
perror(errbuf);
exit(1);
}
/* pcap_lookupnet이 반환하는 net, mask 값은 network address, 즉 정수형으로 이루어져있기에 이 값을 숫자 사이에 점을 찍는 IP주소표시법으로 구성된 ASCII 문자열을 가리키는 문자열 포인터를 리턴하는 inet_ntoa()를 이용하여 일반적인 IP주소 형태로 바꾸어 출력함. */
net_addr.s_addr = net;
mask_addr.s_addr = mask;
printf("Device : %s\n", dev);
printf("Net Address : %s\n", inet_ntoa(net_addr)); // 네트워크 주소(정수형) => ASCII (우리가 알고있는 IP주소)
printf("Netmask : %s\n", inet_ntoa(mask_addr));
// pcap_open_live (NIC, capture MAX byte, timeout, errbuf)).
pcap_open_live is open packet capture interface and that pointer return */
if((pd = pcap_open_live(dev, PCAP_SNAPSHOT, 1, PCAP_TIMEOUT, errbuf)) == NULL) {
perror(errbuf);
exit(1);
}
if(pcap_compile(pd, &fcode, FILTER_RULE, 0, mask) < 0) {
perror(pcap_geterr(pd));
exit(1);
}
if(pcap_setfilter(pd, &fcode) < 0) {
perror(pcap_geterr(pd));
exit(1);
}
// packet read 관련 API에서는 패킷을 읽었을때, Demultiplexing이 되지 않은 완전한 구조의 패킷을 넘겨줌
if(pcap_loop(pd, PCAP_CNT_MAX, packet_view, 0) < 0) {
perror(pcap_geterr(pd));
exit(1);
}
pcap_close(pd);
return 0;
}
void packet_view(unsigned char* user, const struct pcap_pkthdr *h, const unsigned char *p) {
struct ip *iph;
struct tcphdr *tcph;
struct udphdr *udph;
struct ether_header *etherh;
unsigned short e_type;
int cnt;
/* ethernet header와 IP header의 경우 demultiplexing 과정을 거치기 위해 상위 layer의 protocol type을 지정함. ethernet header의 경우 ether_type, IP header는 ip_p가 각 상위 layer의 protocol type을 알려주기 위해 사용됨. 해당 내용은 OSI 7계층의 encapsulation 참조 */
etherh = (struct ether_header *)p;
e_type = ntohs(etherh->ether_type);
printf("----------------------------------------- \n");
printf("[[ Layer 2 :: Ethernet Header ]]\n");
// out Ehternet Header information
printf("[ Destination hardware address :: %02x", etherh->ether_dhost[0]);
for(cnt = 1; cnt < ETHER_ADDR_LEN; cnt++)
printf(":%02x", etherh->ether_dhost[cnt]);
printf("]\n[ Source hardware address :: %02x", etherh->ether_shost[0]);
for(cnt = 1; cnt < ETHER_ADDR_LEN; cnt++)
printf(":%02x", etherh->ether_shost[cnt]);
printf("]\n[ Type :: %x ]\n", etherh->ether_type);
printf("\t----------------------------------------- \n");
p += sizeof(struct ether_header);
// IP, ARP, RARP Header analyze
if (e_type == ETHERTYPE_IP) {
iph = (struct ip *)p;
printf("\t[IP Packet!]\n");
printf("\t----------------------------------------- \n");
printf("\t[ IP version :: %d ]\n", iph->ip_v);
printf("\t[ Header Length :: %d ]\n", iph->ip_hl);
printf("\t[ Type of Service :: %d ]\n", iph->ip_tos);
printf("\t[ Total Length :: %d ]\n", iph->ip_len);
printf("\t[ Identification :: %d ]\n", iph->ip_id);
printf("\t[ Flags ================== :: ]\n");
if ((iph->ip_off) == IP_RF)
printf("\t[ || Reserved flagment :: 0x%x ]\n", IP_RF);
else if((iph->ip_off) == IP_DF)
printf("\t[ || Don't flagment :: 0x%x ]\n", IP_DF);
else if((iph->ip_off) == IP_MF)
printf("\t[ || More flagment :: 0x%x ]\n", IP_MF);
else
printf("\t[ || Not support ]\n");
printf("\t[ ========================== ]\n");
printf("\t[ Fragment Offset :: %d ]\n", iph->ip_off);
printf("\t[ TTL :: %d ]\n", iph->ip_ttl);
printf("\t[ Protocol :: %d ]\n", iph->ip_p);
printf("\t[ Header Checksum :: %d ]\n", iph->ip_sum);
printf("\t[ Source IP address :: %s ]\n", inet_ntoa(iph->ip_src));
printf("\t[ Destination IP address :: %s ]\n", inet_ntoa(iph->ip_dst));
printf("\t----------------------------------------- \n");
/* TCP Header analyze.
Transport layer의 type을 알려주는 ip_p는 <netinet/ip.h>, IPPTORO_TCP의 내용은 <netinet/in.h> 참조. */
if (iph->ip_p == IPPROTO_TCP) {
tcph = (struct tcphdr *)(p + iph->ip_hl *4) ;
printf("\t\t[ TCP Packet!]\n");
printf("\t\t----------------------------------------- \n");
printf("\t\t[ Source Port :: %d ]\n", ntohs(tcph->source));
printf("\t\t[ Destination Port :: %d ]\n", ntohs(tcph->dest));
}
else if (iph->ip_p == IPPROTO_UDP) {
udph = (struct udphdr *)(p + iph->ip_hl *4) ;
printf("\t\t[ UDP Packet!]\n");
printf("\t\t----------------------------------------- \n");
printf("\t\t[ Source Port :: %d ]\n", ntohs(udph->source));
printf("\t\t[ Destination Port :: %d ]\n", ntohs(udph->dest));
}
}
else if(e_type == ETHERTYPE_ARP) {
printf("ARP!!!\n");
}
else if(e_type == ETHERTYPE_REVARP) {
printf("RARP!!!\n");
}
else {
printf("Not Captured packet!\n");
}
}