目标
- 利用PF_PACKET 和SOCK_RAW创建套接字发送一个任意的以太网帧
背景
-
以太网是一个链路层协议。大多数网络程序员关注网络栈的传输层及以上,所以不需要直接处理以太网帧,但是某些场景下关注传输层以下也是有必要的。如:
1)实现网络协议栈里面没有内置的以太网协议类型
2)为测试目的,产生一个畸形或者其它非常规帧
使用场景
- 已知发送方和接收方的mac地址,就可以使用这个以太网协议发送接收数据。
编程
- 使用PF_PACKET 和SOCK_RAW创建套接字
注:用这个套接字的打开需要用户有root权限
if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0){
perror("socket create failed");
exit (EXIT_FAILURE);
}
参数1:PF_PACKET,该参数是从MAC层直接收发原始数据。
参数2:SOCK_RAW,使用该参数需要自己构造以太网数据包。
SOCK_DGRAM,填这个参数是以太网头已经构造好了。
参数3:ETH_P_ALL自身定义于 /usr/include/linux/if_ether.h中,使用它是包含所有协议类型。
2.将网络接口赋值给原始套接字地址结构
memset(&device, 0, sizeof(device));
strncpy(req.ifr_name, interface, IFNAMSIZ); //指定网卡名称
//赋值以太网接口的索引值
ioctl(fd, SIOCGIFINDEX, &req);
bzero(&device, sizeof(device));
device.sll_ifindex = req.ifr_ifindex;
dst_mac[0] = 0x00;
dst_mac[1] = 0x50;
dst_mac[2] = 0x90;
dst_mac[3] = 0xb0;
dst_mac[4] = 0xb0;
dst_mac[5] = 0xb0;
//Fill out sockaddr_ll.
device.sll_family = AF_PACKET;
memcpy(device.sll_addr, dst_mac, 6);
device.sll_halen = htons(6);
device.sll_protocol = htons(ETH_P_ALL);
里面除了以太网接口的索引值,其他赋值都是可以不用的,因为你下面自己会构造以太网包,目标mac会你构造的包中的mac为准。
3.构造协议包
char org_buf[60] = {
0x00, 0x50, 0x90, 0xb0, 0xb0, 0xb0, // 目的mac
0x00, 0x50, 0x90, 0xcc, 0xcc, 0xcc, // 源mac
0x80, 0x01, //以太网协议type
0, 1, 0, 42,
1, 4, 0, 0,
2, 4, 0, 0,
3, 4, 0, 0,
4, 4, 0, 0,
5, 4, 0, 0,
6, 4, 0, 0,
7, 4, 0, 0,
8, 4, 0, 0,
9, 6, 0, 0, 0, 0,
0, 0, 0, 0,
};
前6字节为目的mac,后6字节为源mac,0x8001为以太网协议type。注意,自己要发送的数据要从第21字节开始。
下面是协议类型,我们这里的0x8001是自己定义的。
#define ETH_P_LOOP 0x0060 /* Ethernet Loopbackpacket */
#define ETH_P_PUP 0x0200 /* Xerox PUP packet */
#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN
#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */
#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet*/
4.使用sendto发送帧
int ret = sendto(fd,frame_buf,60,0, (struct sockaddr *)&device,lllen);
if(ret == -1){
perror("sendto error");
}
5.完整代码
int generate_frame(char *buf, GENERATE *generate)
{
// int i = 0;
const char org_buf[60] = {
// 0xB0, 0x5C, 0xDA, 0x62, 0xE0, 0xF5, // 目的mac
// 0x00, 0xd8, 0x61, 0x1a, 0xc3, 0xd9, // 源mac
0xB0, 0x5C, 0xDA, 0x62, 0xE0, 0xF5,
0x00, 0x50, 0x90, 0xcc, 0xcc, 0xcc,
0x80, 0x01, //以太网协议type
// content
0, 1, 0, 42,
1, 4, 0, 0,
2, 4, 0, 0,
3, 4, 0, 0,
4, 4, 0, 0,
5, 4, 0, 0,
6, 4, 0, 0,
7, 4, 0, 0,
8, 4, 0, 0,
9, 6, 0, 0, 0, 0,
0, 0, 0, 0,
// 0, 0, 0, 0,
};
memcpy(buf, org_buf, sizeof(org_buf));
// for(i = 0; i < 6; i++){
// buf[i] = dst_mac[i];
// buf[i+6] = src_mac[i];
// }
putUint16(buf+20+0*4, cnvPlmn(generate->plmn));
putUint16(buf+20+1*4, cnvType(generate->type));
putUint16(buf+20+2*4, cnvRssi(generate->rssi));
putUint16(buf+20+3*4, cnvData(generate->dataEnable));
putUint16(buf+20+4*4, cnvDataErrno(generate->dataErrno));
putUint16(buf+20+5*4, cnvWifi(generate->wifiStat));
putUint16(buf+20+6*4, cnvWifiErrno(generate->wifiErrno));
putUint16(buf+20+7*4, cnvSim(generate->simEnable));
putIP(buf+20+8*4,generate->ip);
return sizeof(org_buf);
}
int main(int argc, char* argv[])
{
int fd = -1;
char frame_buf[1024] = {0};
struct sockaddr_ll device;
// unsigned char src_mac[8] = {0};
unsigned char dst_mac[8] = {0};
int lllen = 0;
struct ifreq req;
struct ifreq ifr;
int i = 0;
GENERATE generate = {0};
char plmn_buf[30] = {0};
char type_buf[30] = {0};
int nrssi = 0;
int nOffset = 0;
char sTarg[4] = {0};
if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0){
perror("socket create failed");
exit (EXIT_FAILURE);
}
memset(&device, 0, sizeof(device));
strncpy(req.ifr_name, interface, IFNAMSIZ); //指定网卡名称
//4.将网络接口赋值给原始套接字地址结构
ioctl(fd, SIOCGIFINDEX, &req);
bzero(&device, sizeof(device));
device.sll_ifindex = req.ifr_ifindex;
dst_mac[0] = 0xB0;
dst_mac[1] = 0x5C;
dst_mac[2] = 0xDA;
dst_mac[3] = 0x62;
dst_mac[4] = 0xE0;
dst_mac[5] = 0xF5;
//Fill out sockaddr_ll.
device.sll_family = AF_PACKET;
memcpy(device.sll_addr, dst_mac, 6);
device.sll_halen = htons(6);
device.sll_protocol = htons(ETH_P_ALL);
while(1){
Sleep(T1);
//电信运营商
cfg_get_item("nv_plmn",plmn_buf,sizeof(plmn_buf));
if(strlen(plmn_buf)){
generate.plmn = atoi(plmn_buf);
}
//网络类型
cfg_get_item("network_type",type_buf,sizeof(type_buf));
if(!strcmp(type_buf, "LTE")){ //4G
generate.type = 3;
}else if(!strcmp(type_buf, "WCDMA")){ //3G
generate.type = 2;
}else if(!strcmp(type_buf, "GSM")){ //2G
generate.type = 1;
}
//rssi
tw_get_rssi(&nrssi);
generate.rssi = nrssi;
memset(type_buf,0,sizeof(type_buf));
//数据连接是否在线
cfg_get_item("pdp_status",type_buf,sizeof(type_buf));
if(atoi(type_buf) == 1){
generate.dataEnable = 2;
generate.dataErrno = 2;
}else{
generate.dataEnable = 1;
generate.dataErrno = 1;
}
//SIM卡状态
memset(type_buf,0,sizeof(type_buf));
cfg_get_item("modem_main_state",type_buf,sizeof(type_buf));
if(!strcmp(type_buf, "modem_init_complete")){
generate.simEnable = 2;
}else{
generate.simEnable = 1;
}
//wifiStat/wifiErrno
generate.wifiStat = 1;
generate.wifiErrno = 2;
//公网IP
generate.ip = 0;
memset(type_buf,0,sizeof(type_buf));
cfg_get_item("wan1_ip",type_buf,sizeof(type_buf));
printf("wan1_ip:%s\r\n",type_buf);
nOffset = 0;
nOffset += StrTok(type_buf+nOffset,sTarg,'.');
generate.ip += atoi(sTarg) << 24;
nOffset += StrTok(type_buf+nOffset,sTarg,'.');
generate.ip += atoi(sTarg) << 16;
nOffset += StrTok(type_buf+nOffset,sTarg,'.');
generate.ip += atoi(sTarg) << 8;
nOffset += StrTok(type_buf+nOffset,sTarg,'.');
generate.ip += atoi(sTarg);
generate_frame(frame_buf,&generate);
lllen = sizeof(struct sockaddr_ll);
int ret = sendto(fd,frame_buf,60,0, (struct sockaddr *)&device,lllen);
if(ret == -1){
perror("sendto error");
}
}
close(fd);
return 0;
}
6.wireshark抓包实测
上面Protocol为0x8001的就是我们在目的mac上抓的包,具体数据也可以看到。