https://blog.csdn.net/ZhipingXi/article/details/54972209
https://blog.csdn.net/dengjin20104042056/article/details/52373903
https://blog.csdn.net/jx232515/article/details/51912700
https://linux.die.net/man/3/ether_ntoa
#include "MrKSocket.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
CSocket::CSocket()
{
m_sockfd = 0;
m_type = NONE;
memset(&m_stListen, 0, sizeof(m_stListen));
m_src_mac = "";
m_dst_mac = "";
m_src_ip = "";
m_dst_ip = "";
m_dst_port = 0;
m_src_port = 0;
m_NIC_name = "";
m_mtu = 0;
m_pkt_id = 0;
}
CSocket::~CSocket()
{
close(m_sockfd);
}
int8_t CSocket::CreateSocket(sockType type)
{
if(type == UDP)
{
m_sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if(m_sockfd == -1)
{
perror("create UDP socket failed");
return 0;
}
m_type = UDP;
}
else if(type == MAC_RAW)
{
m_sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(m_sockfd == -1)
{
perror("create MAC_RAW socket failed");
return 0;
}
m_type = MAC_RAW;
}
uint8_t flag = 1;
setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(uint8_t));
return 1;
}
int8_t CSocket::Listening(uint16_t port, string ip)
{
m_stListen.sin_family = AF_INET;
m_stListen.sin_addr.s_addr = inet_addr(ip.c_str());
m_stListen.sin_port = htons(port);
int16_t res = bind(m_sockfd, (const struct sockaddr*)&m_stListen, sizeof(m_stListen));
if(res == -1)
{
perror("bind failed");
return 0;
}
return 1;
}
void CSocket::ReceiveData()
{
uint8_t buf[1500] = {0};
struct sockaddr_in client;
memset(&client, 0, sizeof(client));
while(1)
{
socklen_t len = sizeof(client);
int16_t res = recvfrom(m_sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &len);
if(res >= 0)
{
cout << buf << endl;
}
else
{
cout << "receive data failed" << endl;
}
}
}
string CSocket::SendArpRequest(string src_ip, string src_mac, string dst_ip)
{
struct arppacket
{
struct ether_header eh;//以太网的头部
struct ether_arp ea;//arp包数据结构
uint8_t padding[18];//填充位,ARP包的最小长度是60个字节,不包括以太网的帧的CRC校验和
} arpreq;
uint32_t l_src_ip = inet_addr(src_ip.c_str());
uint32_t l_dst_ip = inet_addr(dst_ip.c_str());
int16_t sock_arpreq = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP));
if(sock_arpreq == -1)
{
perror("cannot create ARP socket");
close(sock_arpreq);
return NULL;
}
int16_t flag = true;
setsockopt(sock_arpreq, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int16_t));
memset(&arpreq, 0, sizeof(arpreq));
// 以太网头
string dst_mac = "FF:FF:FF:FF:FF:FF";
memcpy(arpreq.eh.ether_dhost, ether_aton(dst_mac.c_str()), ETH_ALEN);// 6 bytes
memcpy(arpreq.eh.ether_shost, ether_aton(src_mac.c_str()), ETH_ALEN);// 6 bytes
arpreq.eh.ether_type = htons(ETHERTYPE_ARP);//协议类型ARP协议 2 bytes
// arp数据包
arpreq.ea.arp_hrd = htons(ARPHRD_ETHER);//硬件类型 2 bytes
arpreq.ea.arp_pro = htons(ETHERTYPE_IP);//协议类型 2 bytes
arpreq.ea.arp_hln = ETH_ALEN;//MAC地址长度6字节 1 byte
arpreq.ea.arp_pln = 4;//IP地址长度 1 byte
arpreq.ea.arp_op = htons(ARPOP_REQUEST);//操作码,ARP请求包 2 bytes
memcpy(arpreq.ea.arp_sha, ether_aton(src_mac.c_str()), ETH_ALEN);//源MAC 6 bytes
memcpy(arpreq.ea.arp_spa, &l_src_ip, 4);//源IP 4 bytes
memcpy(arpreq.ea.arp_tha, ether_aton(dst_mac.c_str()), ETH_ALEN);//目的MAC 6 bytes
memcpy(arpreq.ea.arp_tpa, &l_dst_ip, 4); // 目的ip 4 bytes
struct sockaddr_ll dst_info;
memset(&dst_info, 0, sizeof(dst_info));
dst_info.sll_family = PF_PACKET;
dst_info.sll_ifindex = if_nametoindex(m_NIC_name.c_str());//返回对应接口名的编号
int res = sendto(sock_arpreq,&arpreq,sizeof(arpreq),0,(struct sockaddr*)&dst_info,sizeof(dst_info));//发送arp请求包
if(res == -1)
{
perror("arp request sendto failed");
close(sock_arpreq);
return NULL;
}
//接收ARP响应包
uint8_t buffer[ETH_FRAME_LEN];
memset(buffer, 0, ETH_FRAME_LEN);
struct sockaddr_ll client;
memset(&client, 0, sizeof(client));
socklen_t len=sizeof(client);
int sock_arprecv = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));//只接受发往本机的ARP帧
if(sock_arprecv == -1)
{
perror("create ARP recv socket failed");
close(sock_arpreq);
return NULL;
}
while(1)
{
res = recvfrom(sock_arprecv,buffer,ETH_FRAME_LEN,0,(struct sockaddr*)&client,&len);
if(res == -1)
{
perror("arp request recvfrom failed");
break;
}
else
{
struct ether_header *eHeader=(struct ether_header*)buffer;
dst_mac = ether_ntoa((struct ether_addr*)eHeader->ether_shost);
break;
}
}
close(sock_arpreq);
close(sock_arprecv);
return dst_mac;
}
int8_t CSocket::Init(string nic, uint16_t port)
{
int16_t sock_localhost;
string local_ipaddr;
struct sockaddr_in *sin;
struct ifreq ifr_localhost;
sock_localhost = socket(AF_INET, SOCK_STREAM, 0);
if(sock_localhost == -1)
{
perror("create localhost socket failed");
return 0;
}
// 本地网卡
m_NIC_name = nic;
memset(&ifr_localhost, 0, sizeof(ifr_localhost));
strncpy(ifr_localhost.ifr_name, m_NIC_name.c_str(), sizeof(ifr_localhost.ifr_name)-1);
// 获取本地mtu
if(ioctl(sock_localhost, SIOCGIFMTU, &ifr_localhost) == -1)
{
perror("cannot get local mtu");
close(sock_localhost);
return 0;
}
m_mtu = ifr_localhost.ifr_mtu;
printf("localhost MTU: %d\n", m_mtu);
// 获取本地ip
if(ioctl(sock_localhost, SIOCGIFADDR, &ifr_localhost) == -1)
{
perror("cannot get local ip");
close(sock_localhost);
return 0;
}
sin = (struct sockaddr_in*)&ifr_localhost.ifr_addr;
m_src_ip = inet_ntoa(sin->sin_addr);
printf("src ip: %s\n",m_src_ip.c_str());
// 本地端口
m_src_port = port;
printf("src port: %d\n",m_src_port);
// 获取本地mac
if(ioctl(sock_localhost, SIOCGIFHWADDR, &ifr_localhost) == -1)
{
perror("cannot get local mac");
close(sock_localhost);
return 0;
}
m_src_mac = ether_ntoa((struct ether_addr*)ifr_localhost.ifr_hwaddr.sa_data);
printf("src mac: %s\n", m_src_mac.c_str());
// 用随机数初始化数据包id
srand((int16_t)time(0));
m_pkt_id = rand()%10000;
close(sock_localhost);
return 1;
}
void CSocket::ConnectTo(string ip, uint16_t port)
{
m_dst_ip = ip;
m_dst_port = port;
printf("dst ip: %s\n", m_dst_ip.c_str());
printf("dst port: %d\n", m_dst_port);
// 用arp请求获取目的mac
m_dst_mac = SendArpRequest(m_src_ip,m_src_mac,m_dst_ip);
printf("dst mac: %s\n", m_dst_mac.c_str());
}
void CSocket::SendData(string msg)
{
uint16_t dataLen = strlen(msg.c_str());
if(dataLen % 2 == 1)
{
msg.append("\0");
dataLen++;
}
printf("data length: %d\n",dataLen);
if(m_type == MAC_RAW)
{
struct ether_header ethHeader;
struct ip ipHeader;
struct udphdr udpHeader;
struct psdhdr
{
struct in_addr src_ip;
struct in_addr dst_ip;
uint8_t padding;
uint8_t proto;
uint16_t len;
}psdHeader;
uint8_t ethHdrLen = sizeof(ethHeader);
uint8_t ipHdrLen = sizeof(ipHeader);
uint8_t udpHdrLen = sizeof(udpHeader);
uint8_t psdHdrLen = sizeof(psdHeader);
uint32_t l_ip_src = inet_addr(m_src_ip.c_str());
uint32_t l_ip_dst = inet_addr(m_dst_ip.c_str());
// 以太网头
memcpy(ethHeader.ether_dhost, ether_aton(m_dst_mac.c_str()), 6);// 6 bytes
memcpy(ethHeader.ether_shost, ether_aton(m_src_mac.c_str()), 6);// 6 bytes
ethHeader.ether_type = htons(ETHERTYPE_IP);// 2 bytes
// ip头
ipHeader.ip_v = 4;// 0.5 byte
ipHeader.ip_hl = 5;// 0.5 byte
ipHeader.ip_tos = 0;// 1 byte
ipHeader.ip_len = 0;// 2 byte
ipHeader.ip_id = htons(m_pkt_id);// 2 bytes
ipHeader.ip_off = 0;// 2 bytes
ipHeader.ip_ttl = 64;// 1 byte
ipHeader.ip_p = 17; // 1 byte
ipHeader.ip_sum = 0;// 2 bytes
memcpy(&ipHeader.ip_src, &l_ip_src, 4);// 4 bytes
memcpy(&ipHeader.ip_dst, &l_ip_dst, 4);// 4 bytes
// udp头
udpHeader.source = htons(m_src_port);// 2 bytes
udpHeader.dest = htons(m_dst_port);// 2 bytes
udpHeader.len = htons(udpHdrLen+dataLen);// 2 bytes
udpHeader.check = 0;// 2 bytes
// 伪首部 udp
memcpy(&psdHeader.src_ip, &l_ip_src, 4);// 4 bytes
memcpy(&psdHeader.dst_ip, &l_ip_dst, 4);// 4 bytes
psdHeader.padding = 0;// 1 byte
psdHeader.proto = 17;// 1 byte
psdHeader.len = htons(udpHdrLen+dataLen);// 2 bytes
// udp数据校验
uint8_t* udp_packet = new uint8_t[psdHdrLen+udpHdrLen+dataLen];
memcpy(udp_packet, &psdHeader, psdHdrLen);
memcpy(udp_packet+psdHdrLen, &udpHeader, udpHdrLen);
memcpy(udp_packet+psdHdrLen+udpHdrLen, msg.c_str(), dataLen);
uint16_t udp_sum = htons(CheckSum((uint16_t*)udp_packet, (psdHdrLen+udpHdrLen+dataLen)/2));
udpHeader.check = udp_sum;
struct sockaddr_ll sll;
struct ifreq req;
strncpy(req.ifr_name, m_NIC_name.c_str(), IFNAMSIZ);
int16_t res = ioctl(m_sockfd, SIOCGIFINDEX, &req);
if(res == -1)
{
perror("ioctl");
close(m_sockfd);
return;
}
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = req.ifr_ifindex;
uint32_t msgLen = ethHdrLen+ipHdrLen+udpHdrLen+dataLen;
if(msgLen < ETHER_FRAME_MIN)
{
msgLen = ETHER_FRAME_MIN;
}
uint16_t mtuReal = m_mtu - (m_mtu-ipHdrLen) % 8;// 如果分片,每一个包的数据量都是8的倍数
uint16_t pkt_num = (udpHdrLen+dataLen) / (mtuReal-ipHdrLen) + 1;// 发送的包数量
printf("packet number: %d\n", pkt_num);
uint8_t* ipData = new uint8_t[udpHdrLen+dataLen];// 组ip数据包的数据部分
memset(ipData, 0, udpHdrLen+dataLen);
memcpy(ipData, &udpHeader, udpHdrLen);
memcpy(ipData+udpHdrLen, msg.c_str(), dataLen);
for(uint16_t i=1; i<=pkt_num; i++)
{
uint16_t ipData_len = 0;// 数据报中ip头之后的数据长度(不含ip头)
uint16_t ip_off = 0;
if(i == pkt_num)// 最后一包
{
ipData_len = (udpHdrLen+dataLen) % (mtuReal-ipHdrLen);
ip_off &= 0xdfff;// MF = 0
}
else
{
ipData_len = mtuReal - ipHdrLen;
ip_off |= 0x2000;// MF = 1
}
// 计算ip数据包长度
ipHeader.ip_len = htons(ipData_len+ipHdrLen);
printf("ip packet %d, len: %d\n", i, ipData_len+ipHdrLen);
// 计算偏移量
uint16_t offset = (i-1) * (mtuReal-ipHdrLen) >> 3;
ip_off |= offset;
ipHeader.ip_off = htons(ip_off);
printf("offset: %d\n", offset);
// ip头校验
ipHeader.ip_sum = 0;// 这里一定要先置0
uint8_t* ip_packet = new uint8_t[ipHdrLen];
memset(ip_packet, 0, ipHdrLen);
memcpy(ip_packet, &ipHeader, ipHdrLen);
uint16_t ip_sum = CheckSum((uint16_t*)ip_packet, ipHdrLen/2);
ipHeader.ip_sum = htons(ip_sum);
uint16_t pkt_len = ethHdrLen+ipHdrLen+ipData_len;
uint8_t* send_msg = new uint8_t[pkt_len];
memset(send_msg, 0, pkt_len);
memcpy(send_msg, ðHeader, ethHdrLen);
memcpy(send_msg+ethHdrLen, &ipHeader, ipHdrLen);
memcpy(send_msg+ethHdrLen+ipHdrLen, ipData+(offset<<3), ipData_len);
res = sendto(m_sockfd, send_msg, pkt_len, 0 , (struct sockaddr *)&sll, sizeof(sll));
if(res == -1)
{
perror("sendData sendto");
}
delete ip_packet;
delete send_msg;
}
}
else if(m_type == UDP)
{
struct sockaddr_in dstInfo;
memset(&dstInfo, 0, sizeof(dstInfo));
dstInfo.sin_family = AF_INET;
dstInfo.sin_port = htons(m_dst_port);
dstInfo.sin_addr.s_addr = inet_addr(m_dst_ip.c_str());
connect(m_sockfd, (struct sockaddr*)&dstInfo, sizeof(dstInfo));
int16_t res = send(m_sockfd, msg.c_str(), dataLen, 0);
//int16_t res = sendto(m_sockfd, msg.c_str(), dataLen, 0, (struct sockaddr*)&dstInfo, sizeof(dstInfo));
if(res == -1)
{
perror("udp sendto failed");
}
}
m_pkt_id++;
}
/*
uint16_t CSocket::CheckSum(uint8_t* buf, uint16_t len)
{
uint32_t sum;
for(sum=0; len>0; len--)
{
sum += *buf;
buf++;
}
sum = (sum>>16) + (sum&0xffff);
sum += (sum>>16);
return ~sum;
}*/
// 校验一定要用16位的指针,求和时要把主机字节序转成网络字节序
uint16_t CSocket::CheckSum(uint16_t* buf, uint16_t len)
{
uint32_t sum;
for(sum=0; len>0; len--)
{
sum += htons(*buf);
buf++;
}
sum = (sum>>16) + (sum&0xffff);
sum += (sum>>16);
return ~sum;
}