TCP socket,UDP socket只能处理传输层数据,而
原始套接字raw socket绕过传输层直接获取网络链路层的数据包。我们平时用的tcpdump, libpcap都是基于raw socket实现的。
一般使用方法如下:
// raw socket
int rawsock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
if (rawsock < 0)
{
perror("SOCK_RAW error");
return rawsock;
}
// set the socket to non-blocking mode
const int flags = fcntl(rawsock, F_GETFL, 0);
if (flags < 0)
{
perror("fcntl F_GETFL error");
return -1;
}
if (fcntl(rawsock, F_SETFL, flags | O_NONBLOCK) < 0)
{
perror("fcntl F_SETFL error");
return -1;
}
// set recv buf len
int buflen = 0;
socklen_t len = sizeof(int);
if (getsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &buflen, &len) < 0)
{
perror("Error getsockopt SO_RCVBUF: %s", strerror(errno));
}
buflen = 10 * 1024 * 1024;
if (setsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(int)) < 0)
{
perror("Error setsockopt SO_RCVBUF: %s", strerror(errno));
}
if (getsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &buflen, &len) < 0)
{
perror("Error getsockopt SO_RCVBUF: %s", strerror(errno));
}
// get interface index
struct ifreq ifr;
bzero(&ifr, sizeof(struct ifreq));
if (ifname.size() >= IFNAMSIZ)
{
perror("%s interface name is too long", ifname.c_str());
return -1;
}
strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
if (ioctl(rawsock, SIOCGIFINDEX, &ifr) < 0)
{
perror("ioctl SIOCGIFINDEX error");
return -1;
}
// set promisc mode
struct packet_mreq mreq;
bzero(&mreq, sizeof(struct packet_mreq));
mreq.mr_ifindex = ifr.ifr_ifindex;
mreq.mr_type = PACKET_MR_PROMISC;
if (setsockopt(rawsock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
perror("setsockopt PACKET_ADD_MEMBERSHIP error");
return -1;
}
// bind
struct sockaddr_ll saddr;
bzero(&saddr, sizeof(struct sockaddr_ll));
saddr.sll_family = AF_PACKET;
saddr.sll_protocol = htons(ETH_P_IP);
saddr.sll_ifindex = ifr.ifr_ifindex;
if (bind(rawsock, (struct sockaddr*)(&saddr), sizeof(saddr)) < 0)
{
perror("bind error");
return -1;
}
参考:
https://www.opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/