linux下的类似wireshark、tcpdump的IP抓包程序
很久写的个小程序,可以实现类似wireshark、tcpdump的抓包分析功能:
/*linux下用socket的抓包程序*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <string.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/if_ether.h>
#include <net/ethernet.h>
/*
#define ETH_ALEN 6
************************eth的结构**************************************
struct ether_header
{
u_int8_t ether_dhost[ETH_ALEN]; // destination eth addr
u_int8_t ether_shost[ETH_ALEN]; // source ether addr
u_int16_t ether_type; // packet type ID field
} __attribute__ ((__packed__));
***********************IP的结构***********************************
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error “Please fix <bits/endian.h>”
#endif
u_int8_t tos;
u_int16_t tot_len;
u_int16_t id;
u_int16_t frag_off;
u_int8_t ttl;
u_int8_t protocol;
u_int16_t check;
u_int32_t saddr;
u_int32_t daddr;
};
***********************TCP的结构****************************
struct tcphdr
{
u_int16_t source;
u_int16_t dest;
u_int32_t seq;
u_int32_t ack_seq;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int16_t res1:4;
u_int16_t doff:4;
u_int16_t fin:1;
u_int16_t syn:1;
u_int16_t rst:1;
u_int16_t psh:1;
u_int16_t ack:1;
u_int16_t urg:1;
u_int16_t res2:2;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int16_t doff:4;
u_int16_t res1:4;
u_int16_t res2:2;
u_int16_t urg:1;
u_int16_t ack:1;
u_int16_t psh:1;
u_int16_t rst:1;
u_int16_t syn:1;
u_int16_t fin:1;
#else
#error “Adjust your <bits/endian.h> defines”
#endif
u_int16_t window;
u_int16_t check;
u_int16_t urg_ptr;
};
***********************UDP的结构*****************************
struct udphdr
{
u_int16_t source;
u_int16_t dest;
u_int16_t len;
u_int16_t check;
};
*/
void die(char *why, int n)
{
perror(why);
exit(n);
}
/*修改网卡成PROMISC(混杂)模式*/
int do_promisc(char *nif, int sock )
{
struct ifreq ifr;
strncpy(ifr.ifr_name, nif,strlen(nif)+1);
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) //获得flag
{
die(“NET_CAPTURE: ioctl get SIOCGIFFLAGS error!\n”, 2);
}
ifr.ifr_flags |= IFF_PROMISC; //重置flag标志
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) //改变模式
{
die(“NET_CAPTURE: ioctl: set IFF_PROMISC error!\n”, 3);
}
}
void main(int argc,char *argv[])
{
struct sockaddr_in addr,remote_addr;
struct ether_header *peth;
struct iphdr *pip;
struct tcphdr *ptcp;
struct udphdr *pudp;
char mac[16];
int i,sock, r, len;
int sendSock;
char *data;
char *ptemp;
char ss[32] = {0}, dd[32] = {0};
unsigned short ssPort,ddPort;
unsigned short monPort1,monPort2,monPort3,monPort4,monPort5;
unsigned short sendPort;
char *if_name,*sendIp;
int isDebug=0;
if(argc != 9)
{
printf(“NET_CAPTURE: COMMAND LINE FORMAT:\n”);
printf(” net_caputure if_name sendIp sendPort monPort1 monPort2 monPort3 monPort4 monPort5\n”);
printf(” if_name: captured intface name: eth0, bond0, br0\n”);
printf(” sendIp: send captured packages to this IP address\n”);
printf(” sendPort: send captured packages to this IP address’s UDP port\n”);
printf(” monPortX: capture these port’s UDP/TCP packages\n”);
printf(” if monPort1 is 0, capture all UDP/TCP packages in this if_name\n”);
printf(” EXAMPLE: ./net_caputure eth0 172.16.154.250 5555 80 8080 8443 8000 3306\n”);
die(“NET_CAPTURE: parameter is error\n”, 1);
}
if_name = argv[1];
sendIp = argv[2];
sendPort = (unsigned short)atoi(argv[3]);
monPort1 = (unsigned short)atoi(argv[4]);
monPort2 = (unsigned short)atoi(argv[5]);
monPort3 = (unsigned short)atoi(argv[6]);
monPort4 = (unsigned short)atoi(argv[7]);
monPort5 = (unsigned short)atoi(argv[8]);
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family=AF_INET;
remote_addr.sin_addr.s_addr=inet_addr(sendIp);
remote_addr.sin_port=htons(sendPort);
/*建立socket, man socket可以看到上面几个宏的意思*/
if((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1)
{
die(“NET_CAPTURE: create monitor socket error!\n”, 1);
}
if((sendSock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
die(“NET_CAPTURE: create sending socket error!\n”, 1);
}
/*eth0为网卡名称 */
do_promisc(if_name, sock);
system(“ifconfig”);
printf(“\n”);
printf(“NET_CAPTURE: Use \”touch net_capture.debug\” to output debug information\n”);
printf(“NET_CAPTURE: Use \”rm net_capture.debug\” to shutdown debug information\n”);
printf(“\n”);
char buf[65536+40] = {0};
for(;;)
{
/*Use “touch net_capture.debug” to output debug information*/
/*Use “rm net_capture.debug” to shutdown debug information*/
if(access(“net_capture.debug”, F_OK)==0)
isDebug = 1;
else
isDebug = 0;
len = sizeof(addr);
r = recvfrom(sock, (char *)buf, sizeof(buf), 0, (struct sockaddr *)&addr, (socklen_t *)&len);
/*调试的时候可以增加一个输出r的语句判断是否抓到包*/
buf[r] = 0;
ptemp = buf;
peth = (struct ether_header *)ptemp;
if(r <= sizeof(struct ether_header)) /*only ethernet package*/
continue;
if(ntohs(peth->ether_type)!=0x0800)
continue;
/*指针后移eth头的长度*/
ptemp += sizeof(struct ether_header);
/*pip指向ip层的包头*/
/*pip = (struct ip *)ptemp;*/
pip = (struct iphdr *)ptemp;
if(r <= sizeof(struct ether_header)+sizeof(struct iphdr)) /*only ip package*/
continue;
/*指针后移ip头的长度 */
ptemp += sizeof(struct ip);
/*根据不同协议判断指针类型*/
switch(pip->protocol)
{
case IPPROTO_TCP:
{
/*ptcp指向tcp头部*/
ptcp = (struct tcphdr *)ptemp;
strcpy(ss, inet_ntoa(*(struct in_addr*)&(pip->saddr)));
strcpy(dd, inet_ntoa(*(struct in_addr*)&(pip->daddr)));
ssPort = ntohs(ptcp->source);
ddPort = ntohs(ptcp->dest);
if(monPort1 == 0
|| ssPort == monPort1 || ssPort == monPort2 || ssPort == monPort3 || ssPort == monPort4 || ssPort == monPort5
|| ddPort == monPort1 || ddPort == monPort2 || ddPort == monPort3 || ddPort == monPort4 || ddPort == monPort5)
{
if(isDebug == 1)
printf(“TCP pkt: FROM [%15s:%-5d] TO [%15s:%-5d], len:%d\n”,
ss, ssPort,
dd, ddPort,
r);
if((sendto(sendSock, buf, r, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)))<0)
{
printf(“NET_CAPTURE: sendto error\n”);
close(sock);
close(sendSock);
return;
}
}
break;
}
case IPPROTO_UDP:
{
/*pudp指向udp头部*/
pudp = (struct udphdr *)ptemp;
strcpy(ss, inet_ntoa(*(struct in_addr*)&(pip->saddr)));
strcpy(dd, inet_ntoa(*(struct in_addr*)&(pip->daddr)));
ssPort = ntohs(pudp->source);
ddPort = ntohs(pudp->dest);
if(monPort1 == 0
|| ssPort == monPort1 || ssPort == monPort2 || ssPort == monPort3 || ssPort == monPort4 || ssPort == monPort5
|| ddPort == monPort1 || ddPort == monPort2 || ddPort == monPort3 || ddPort == monPort4 || ddPort == monPort5)
{
if(strcmp(dd,sendIp) != 0 || ddPort != sendPort)
{
if(isDebug == 1)
printf(“UDP pkt: FROM [%15s:%-5d] TO [%15s:%-5d], len:%d, payload len:%d\n”,
ss,
ssPort,
dd,
ddPort,
r,
ntohs(pudp->len)
);
if((sendto(sendSock, buf, r, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)))<0)
{
printf(“NET_CAPTURE: sendto error\n”);
close(sock);
close(sendSock);
return;
}
}
}
break;
}
case IPPROTO_ICMP:
{
strcpy(ss, inet_ntoa(*(struct in_addr*)&(pip->saddr)));
strcpy(dd, inet_ntoa(*(struct in_addr*)&(pip->daddr)));
if(isDebug == 1)
printf(“ICMP pkt: FROM [%s] TO [%s]\n”, ss, dd);
break;
}
case IPPROTO_IGMP:
{
if(isDebug == 1)
printf(“IGMP pkt:\n”);
break;
}
case 81:
{
if(isDebug == 1)
printf(“VMTP(Virtual Machine Terminal Protocol) pkt:\n”);
break;
}
default:
{
if(isDebug == 1)
printf(“Unkown pkt, protocl:%d\n”, pip->protocol);
break;
}
} /*end switch*/
/*perror(“dump”);*/
}
close(sock);
close(sendSock);
}