【Linux网络】Linux网络协议栈常用代码片段
1.读取网卡mac地址
可以通过ioctl(sock,SIOCGIFHHWADDR,&ifr)读取mac地址,对任意类型的socket都适用,只需指定第三参数struct ifreq ifr的ifr.ifr_name,这个ifr_name就是网络设备的名字,如eth0,eth1,lo等,在/proc/net/dev可找到,ioctl通过ifr_name获取设备信息。
struct ifreq ifr;
strncpy(ifr.ifr_name,name,6);
if(ioctl(sock,SIOCGIFHHWADDR,&ifr) < 0)
{
perror("get_hwaddr ioctl:");
close(sock);
return -1;
}
具体实现:
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h> // to avoid warning at inet_ntoa
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main(int argc,char* argv[])
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
unsigned char hwaddr[6] = {0};
char *name = "enp0s3";
if(name == NULL || hwaddr == NULL)
{
printf("get_hwaddr:NULL para\n");
//return -1;
}
sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
printf("get_hwaddr:socket error");
}
// get eth1 mac addr
memset(hwaddr,0,6);
memset(&ifr,0,sizeof(ifr));
strncpy(ifr.ifr_name,name,6);
if(ioctl(sock,SIOCGIFHWADDR,&ifr) < 0)
{
perror("get_hwaddr ioctl:");
close(sock);
return -1;
}
else
{
memcpy(hwaddr,ifr.ifr_hwaddr.sa_data,6);
printf("hwaddr:");
printhex(hwaddr, 6, ":");
}
memset(memzero,0,6);
if(memcmp(memzero,hwaddr,6) == 0)
{
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
运行结果:
hwaddr: 8 : 0 : 27 : c9 : 2e : 9b
2.raw socket收发底层网络数据包
2.1 创建raw socket
int sock = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock < 0)
{
perror("sock");
return -1;
}
第三个参数htons(ETH_P_ALL)只对recvfrom有意义。用这个socket发送的数据,都需要自己维护数据包协议首部,包括网络数据包中的mac地址。
2.2 发送数据包
2.2.1 发送数据包函数原型
int sendto(int s,const void *buf,int len,unsigned int flags,
const struct sockaddr *to,int tolen);
2.2.2 返回值
成功则返回实际传送出去的字符数,失败返回-1,错误原因会存在于errno中
2.2.3 参数说明
s:socket描述符;
buf:rawsocket数据包缓存区(包含待发送数据)
len:rawsocket数据包的长度
flags:调用方式标志位(一般设置为0)
to:指向接收数据的主机地址信息的结构体(sockaddr_in需要进行类型转换)
tolen: to所指结构体的长度
2.2.4 使用方法
struct sockaddr_ll sll;
memset(&sll,0,sizeof(sll));
sll.sll_ifindex = 2; // 指定网卡
if(sendto(sock,packet_start,sizeof(packet_start),0,&sll,sizeof(sll)) < 0)
{
perror("sendto");
return 1;
}
sendto发送原始数据包,只需用struct sockaddr_ll的sll_ifindex指定网卡。
2.2.5 代码实例
发送raw socket(rawsocket_send.c):
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <features.h> /* for the glibc version number */
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#include <stdio.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#define _PATH_PROCNET_DEV "/proc/net/dev"
static char *get_name(char *name, char *p)
{
while (isspace(*p))
p++;
while (*p) {
if (isspace(*p))
break;
if (*p == ':') { /* could be an alias */
char *dot = p, *dotname = name;
*name++ = *p++;
while (isdigit(*p))
*name++ = *p++;
if (*p != ':') { /* it wasn't, backup */
p = dot;
name = dotname;
}
if (*p == '\0')
return NULL;
p++;
break;
}
*name++ = *p++;
}
*name++ = '\0';
return p;
}
/**
* read_netdev_proc - read net dev names form proc/net/dev
* @devname: where to store dev names, devname[num][len]
*/
static int read_netdev_proc(void *devname, const int num, const int len)
{
FILE *fh;
char buf[512];
int cnt = 0;
char *dev = (char *)devname;
if(devname == NULL || num < 1 || len < 4){
printf("read_netdev_proc: para error\n");
return -1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return -1;
}
fgets(buf, sizeof buf, fh); /* eat two line */
fgets(buf, sizeof buf, fh);
cnt = 0;
while (fgets(buf, sizeof buf, fh) && cnt < num) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return 0;
}
/**
* get_hwaddr - get netdevice mac addr
* @name: device name, e.g: eth0
* @hwaddr: where to save mac, 6 byte hwaddr[6]
* @return: 0 on success, -1 on failure
*/
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
unsigned char packet_start[]={
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,//dst mac
0x00, 0x23, 0x54, 0x0e, 0xe5, 0xd8,//src mac
0x88, 0x8e, //Type: 802.1x authentication
0x01, //Version:v1
0x01, //Type: Start (1)
0x00, 0x00//Length 0
};
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main(int argc, char **argv)
{
int i;
unsigned char hwaddr[6];
char devname[3][7];
unsigned char buf[1024]; // for revevied packet
int ret;
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = 2; // It seems only need this to specify whiAWSch NIC to use
memcpy(packet_start + 6, hwaddr, 6);
while(1){
if (sendto(sock, packet_start, sizeof packet_start, 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
printf("Sendto Success!\n");
sleep(1);
}
return 0;
}
2.3 接收数据包
2.3.1 接收数据包函数原型
int recvfrom(int s,void *buf,int len,unsigned int flags,
struct sockaddr *from,int *fromlen)
2.3.2 返回值
成功则返回实际接收到的字符数,失败返回-1,错误原因存于errno中。
2.3.3 参数说明
s: socket描述符
buf: rawsocket数据报缓存区(包含所接收的数据)
len: 缓冲区长度
flags: 调用操作方式(一般设置为0)
from:指向发送数据的客户端地址信息的结构体(sockaddr_in需类型和转换)
fromlen:指针,指向from结构体长度值
2.3.4 使用方法
ret = recvfrom(sock,buf,1024,0,NULL,NULL);
2.3.5 代码示例
接收raw socket(rawsocket_recv.c):
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <features.h> /* for the glibc version number */
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#include <stdio.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main(int argc, char **argv)
{
int i;
unsigned char hwaddr[6];
char devname[3][7];
unsigned char buf[1024]; // for revevied packet
int ret;
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
while(1){
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
printf("recv: ");
printhex(buf, ret, " ");
}
return 0;
}
将以上两份代码编译生成两个可执行程序:
gcc rawsocket_send.c -o rawsocket_send
gcc rawsocket_recv.c -o rawsocket_recv
手动执行收发程序:
sudo ./rawsocket_send
sudo ./rawsocket_recv
2.4 收发实例运行
发送端

接收端

wireshark抓取报文

参考文章:
读取linux下的网络设备的mac地址与发送原始数据包_linux检查报文源mac-CSDN博客
UDP协议 sendto 和 recvfrom 浅析与示例 - 阳光长脸兽 - 博客园 (cnblogs.com)
3.TCP进行文件收发
代码实现在Linux环境下编写测试
3.1 TCP协议收发部分
tcpsocket.h
#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H
#include <iostream>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <net/if.h>
#include <string.h>
#include <arpa/inet.h>
#define SOCKET int
#define PORT 5401
//服务器:创建服务器socket
SOCKET create_serverSocket();
//客户端:创建客户端socket
SOCKET create_clientSocket(const char *ip);
#endif
tcpsocket.cpp
#include "tcpsocket.h"
using namespace std;
SOCKET create_serverSocket()
{
int listenfd_nm;
struct sockaddr_in serv_addr;
listenfd_nm = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd_nm == -1) {
printf("init socket fail!");
sleep(1);
}
int opt = 0;
int ret = setsockopt(listenfd_nm, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //SO_REUSEADDR允许重用本地地址端口
if (ret == -1) {
printf("SO_REUSEADDR fail, errno: %d, %s", errno, strerror(errno));
close(listenfd_nm);
return -1;
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
ret = bind(listenfd_nm, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret == -1) {
printf("bind fail, errno: %d, %s", errno, strerror(errno));
close(listenfd_nm);
return -1;
}
ret = listen(listenfd_nm, 5);
if (ret == -1) {
printf("listen fail, errno: %d, %s", errno, strerror(errno));
close(listenfd_nm);
return -1;
}
return listenfd_nm;
}
SOCKET create_clientSocket(const char *ip)
{
//1.创建一个空的socket
SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == fd)
{
printf("socket");
return -1;
}
//2.给socket绑定服务端ip地址和端口号
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT); //htons():把本地字节序转成网络字节序
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //绑定服务器ip,此处与本机另一进程通信,故ip为本机地址127.0.0.1
if (-1 == connect(fd, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("connect");
return -1;
}
return fd;
}
3.2 文件读写部分
fileOperation.h
/*------------------------------fileOperation.h------------------------------*/
#ifndef __FILEOP_H_
#define __FILEOP_H_
#include "tcpsocket.h"
/***服务端***/
//发送文件
bool sendFile(SOCKET s, const char* fileName);
/***客户端***/
//接受文件
bool recvFile(SOCKET s, const char* fileName);
#endif
fileOperation.cpp
#include "fileOperation.h"
#include <iostream>
using namespace std;
long bufSize = 10*1024;
char *buffer;
// long recvSize = 10000000;
// char* recvBuf;
bool sendFile(int s,const char* fileName)
{
FILE* readfile = fopen(fileName,"rb");
if(!readfile)
{
perror("file open failed:\n");
return false;
}
// get file size
fseek(readfile,0,SEEK_END); // 将文件位置指针移动到最后
bufSize = ftell(readfile); // ftell(FILE *stream):返回给定流stream的当前文件位置,获取当前位置相对文件首的位移,位移值等于文件所含字节数
fseek(readfile,0,SEEK_SET); // 将文件位置指针移动到开头
cout << "filesize:" << bufSize <<endl;
buffer = new char[bufSize];
cout << sizeof(buffer) <<endl;
if(!buffer)
{
return false;
}
int nCount;
int ret = 0;
while((nCount = fread(buffer,1,bufSize,readfile)) > 0)
{
ret += send(s,buffer,nCount,0);
if(ret == -1)
{
printf("sendFile");
return false;
}
}
shutdown(s,SHUT_RDWR);
fclose(readfile);
cout << "send file success!"<<" Byte:"<<ret << endl;
return true;
}
bool recvFile(int s, const char *fileName)
{
if(buffer == NULL)
{
buffer = new char [bufSize];
if(!buffer)
return false;
}
FILE* write = fopen(fileName,"wb");
if(!write)
{
perror("file write failed:\n");
return false;
}
int ret = 0;
int nCount;
while((nCount = recv(s,buffer,bufSize,0)) > 0)
{
ret += fwrite(buffer,nCount,1,write);
}
if(ret == 0)
{
cout << "server offline" <<endl;
}
else if(ret < 0)
{
printf("recv");
return false;
}
cout << "receive file success!" << endl;
fclose(write);
cout << "save file success! Filename:"<<fileName << endl;
return true;
}
3.3 服务器主函数
server_main.cpp
#include "tcpsocket.h"
#include "fileOperation.h"
#include <iostream>
#include<string>
using namespace std;
char* sendBuf;
int main()
{
SOCKET serfd = create_serverSocket();//创建服务器socket(该socket仅用于监听)
cout << "server create success,wait client connect..." << endl;
//等待客户端连接
sockaddr_in caddr;
caddr.sin_family = AF_INET;
socklen_t caddrlen = sizeof(sockaddr_in);
SOCKET clifd = accept(serfd, (sockaddr*)&caddr, &caddrlen); //该socket用于与客户端进行连接
if (clifd == -1)
{
printf("accept");
}
cout << "connect success" << endl;
//可以与客户端进行通信
char fileName[100] = { 0 };
cout << "please input the full path of the file: "<<endl;
cin >> fileName;
sendFile(clifd,fileName);
close(clifd);
close(serfd);
return 0;
}
3.4 客户端主函数
client_main.cpp
#include"tcpsocket.h"
#include"fileOperation.h"
#include<iostream>
using namespace std;
int main()
{
SOCKET fd = create_clientSocket("127.0.0.1");
cout << "connect success!" << endl;
//接收服务器传输的数据
char fileName[100] = { 0 };
cout << "input filename to save:" << endl;
cin>>fileName;
recvFile(fd, fileName);
close(fd);
return 0;
}
3.5 编译及运行
g++ client_main.cpp fileOperation.cpp tcpsocket.cpp -o client
g++ server_main.cpp fileOperation.cpp tcpsocket.cpp -o server
先执行server然后再执行client,然后再client端输入即将存储的文件名,server端输入要发送的文件名的完整路径,即可将文件从server端发送到client端:


4.rawSocket进行文件收发
4.1 rawSocket发送指定路径的文件
rawsocket.h
#ifndef _RAWSOCKET_H_
#define _RAWSOCKET_H_
//#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
//#include <arpa/inet.h>
#include <features.h> /* for the glibc version number */
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#include <stdio.h>
//#include <netinet/in.h>
//#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <linux/if_packet.h>
#include <linux/if.h>
#define SRCMAC_LEN 6
#define DSTMAC_LEN 6
#define PROTOTYPE_LEN 2
unsigned char packet_start[]={
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,//dst mac
0x00, 0x23, 0x54, 0x0e, 0xe5, 0xd8,//src mac
0x88, 0x8e, //Type: 802.1x authentication
0x01, //Version:v1
0x01, //Type: Start (1)
0x00, 0x00//Length 0
};
unsigned char packet_type[]={
0x08,0x06, //Type: 802.1x authentication
};
#endif
rawsocket_sendfile.c
#include "rawsocket.h"
#define _PATH_PROCNET_DEV "/proc/net/dev"
static char *get_name(char *name, char *p)
{
while (isspace(*p))
p++;
while (*p) {
if (isspace(*p))
break;
if (*p == ':') { /* could be an alias */
char *dot = p, *dotname = name;
*name++ = *p++;
while (isdigit(*p))
*name++ = *p++;
if (*p != ':') { /* it wasn't, backup */
p = dot;
name = dotname;
}
if (*p == '\0')
return NULL;
p++;
break;
}
*name++ = *p++;
}
*name++ = '\0';
return p;
}
/**
* read_netdev_proc - read net dev names form proc/net/dev
* @devname: where to store dev names, devname[num][len]
*/
static int read_netdev_proc(void *devname, const int num, const int len)
{
FILE *fh;
char buf[512];
int cnt = 0;
char *dev = (char *)devname;
if(devname == NULL || num < 1 || len < 4){
printf("read_netdev_proc: para error\n");
return -1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return -1;
}
fgets(buf, sizeof buf, fh); /* eat two line */
fgets(buf, sizeof buf, fh);
cnt = 0;
while (fgets(buf, sizeof buf, fh) && cnt < num) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return 0;
}
/**
* get_hwaddr - get netdevice mac addr
* @name: device name, e.g: eth0
* @hwaddr: where to save mac, 6 byte hwaddr[6]
* @return: 0 on success, -1 on failure
*/
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main()
{
int i;
unsigned char hwaddr[SRCMAC_LEN];
unsigned char hwaddr_dst[DSTMAC_LEN] = {0x54,0x6C,0xEB,0x2D,0x3D,0xC2};
char devname[3][7];
int ret;
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
#if 0
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = 2; // It seems only need this to specify whiAWSch NIC to use
#else
struct sockaddr_ll sll;
memset(&sll,0,sizeof(sll));
sll.sll_family = AF_PACKET;//填写AF_PACKET,不再经协议层处理
sll.sll_protocol = htons(ETH_P_ALL);
#if 0
struct ifreq req;
int sd;
sd = socket(PF_INET,SOCK_DGRAM,0); //这个sd就是用来获取eth0的index,完了就关闭
strncpy(req.ifr_name,"enp0s3",4); //通过设备名称获取index
ret=ioctl(sd,SIOCGIFINDEX,&req);
close(sd);
if (ret==-1)
{
printf("Level:%d [Searcher main]Get eth0 index err \n", ret);
}
#endif
sll.sll_ifindex = 2; //网卡eth0的index,非常重要,系统把数据往哪张网卡上发,就靠这个标识
sll.sll_pkttype = PACKET_OUTGOING; //标识包的类型为发出去的包
// 以下配置非必须
sll.sll_halen = DSTMAC_LEN; //目标MAC地址长度为6
//填写目标MAC地址
sll.sll_addr[0] = 0x54;
sll.sll_addr[1] = 0x6C;
sll.sll_addr[2] = 0xEB;
sll.sll_addr[3] = 0x2D;
sll.sll_addr[4] = 0x3D;
sll.sll_addr[5] = 0xC2;
#endif
long bufSize = 0;
FILE* readfile = fopen("/home/user/Desktop/codes/my_em/my-embedded/app/NET/RawSocket/filename.txt","rb");
if(!readfile)
{
perror("file open failed:\n");
return -1;
}
// get file size
fseek(readfile,0,SEEK_END); // 将文件位置指针移动到最后
bufSize = ftell(readfile); // ftell(FILE *stream):返回给定流stream的当前文件位置,获取当前位置相对文件首的位移,位移值等于文件所含字节数
fseek(readfile,0,SEEK_SET); // 将文件位置指针移动到开头
printf("filesize:%ld\n",bufSize);
// unsigned char buffer[1024] = {0};
unsigned char *buffer = (unsigned char*)malloc(sizeof(unsigned char)*bufSize+8);
if(!buffer)
{
return -1;
}
// dst mac:6 src mac:6
memcpy(buffer,hwaddr_dst,DSTMAC_LEN); // dst mac
memcpy(buffer+DSTMAC_LEN,hwaddr,SRCMAC_LEN); // src mac
memcpy(buffer+DSTMAC_LEN+SRCMAC_LEN,packet_type,PROTOTYPE_LEN); // proto type
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
int nCount;
while((nCount = fread(&buffer[DSTMAC_LEN+SRCMAC_LEN+PROTOTYPE_LEN],1,bufSize,readfile)) > 0)
{
if (sendto(sock, buffer, sizeof(unsigned char)*bufSize+8, 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
printf("Sendto Success!\n");
}
free(buffer);
return 0;
}
wireshark抓取报文情况:

4.2 rawSocket接收文件后存储到指定路径
rawsocket_recvfile.c
#include "rawsocket.h"
#define _PATH_PROCNET_DEV "/proc/net/dev"
static char *get_name(char *name, char *p)
{
while (isspace(*p))
p++;
while (*p) {
if (isspace(*p))
break;
if (*p == ':') { /* could be an alias */
char *dot = p, *dotname = name;
*name++ = *p++;
while (isdigit(*p))
*name++ = *p++;
if (*p != ':') { /* it wasn't, backup */
p = dot;
name = dotname;
}
if (*p == '\0')
return NULL;
p++;
break;
}
*name++ = *p++;
}
*name++ = '\0';
return p;
}
/**
* read_netdev_proc - read net dev names form proc/net/dev
* @devname: where to store dev names, devname[num][len]
*/
static int read_netdev_proc(void *devname, const int num, const int len)
{
FILE *fh;
char buf[512];
int cnt = 0;
char *dev = (char *)devname;
if(devname == NULL || num < 1 || len < 4){
printf("read_netdev_proc: para error\n");
return -1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return -1;
}
fgets(buf, sizeof buf, fh); /* eat two line */
fgets(buf, sizeof buf, fh);
cnt = 0;
while (fgets(buf, sizeof buf, fh) && cnt < num) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return 0;
}
/**
* get_hwaddr - get netdevice mac addr
* @name: device name, e.g: eth0
* @hwaddr: where to save mac, 6 byte hwaddr[6]
* @return: 0 on success, -1 on failure
*/
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
int main()
{
int i;
unsigned char hwaddr[6];
char devname[3][7];
unsigned char buf[1024]; // for revevied packet
const char *fileName = "abc.bin";
int ret = 0;
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
#if 1
unsigned char packet_type[] = {0x08,0x06};
int nCount;
while(1){
memset(buf,0,1024);
while((nCount = recvfrom(sock,buf,1024,0,NULL,NULL)) > 0)
{
printf("nCount:%d\n",nCount);
if(0 == memcmp(packet_type,&buf[12],2))
{
FILE* write = fopen(fileName,"a+");
if(!write)
{
perror("file write failed:\n");
return -1;
}
printf("same recv: ");
printhex(&buf[14], nCount-12, " ");
// len要-12去6个字节的srcmac和6个字节的dstmac
ret += fwrite(&buf[14],nCount-14,1,write);
// printf("receive file success!\n");
fclose(write);
}
}
}
#else
while(1){
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
printf("recv: ");
printhex(buf, ret, " ");
}
#endif
return 0;
}
接收存储文件的内容:

5.rawSocket Loop
rawsocket_sendrecv.c
#include "rawsocket_sendrecv.h"
char *get_name(char *name, char *p)
{
while (isspace(*p))
p++;
while (*p) {
if (isspace(*p))
break;
if (*p == ':') { /* could be an alias */
char *dot = p, *dotname = name;
*name++ = *p++;
while (isdigit(*p))
*name++ = *p++;
if (*p != ':') { /* it wasn't, backup */
p = dot;
name = dotname;
}
if (*p == '\0')
return NULL;
p++;
break;
}
*name++ = *p++;
}
*name++ = '\0';
return p;
}
/**
* read_netdev_proc - read net dev names form proc/net/dev
* @devname: where to store dev names, devname[num][len]
*/
int read_netdev_proc(void *devname, const int num, const int len)
{
FILE *fh;
char buf[512];
int cnt = 0;
char *dev = (char *)devname;
if(devname == NULL || num < 1 || len < 4){
printf("read_netdev_proc: para error\n");
return -1;
}
memset(devname, 0, len * num);
fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",
_PATH_PROCNET_DEV, strerror(errno));
return -1;
}
fgets(buf, sizeof buf, fh); /* eat two line */
fgets(buf, sizeof buf, fh);
cnt = 0;
while (fgets(buf, sizeof buf, fh) && cnt < num) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
strncpy(devname, name, len);
devname += len;
printf("get_name: %s\n", name);
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
}
fclose(fh);
return 0;
}
/**
* get_hwaddr - get netdevice mac addr
* @name: device name, e.g: eth0
* @hwaddr: where to save mac, 6 byte hwaddr[6]
* @return: 0 on success, -1 on failure
*/
int get_hwaddr(char *name, unsigned char *hwaddr)
{
struct ifreq ifr;
unsigned char memzero[6];
int sock;
if(name == NULL || hwaddr == NULL){
printf("get_hwaddr: NULL para\n");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("get_hwaddr: socket error\n");
//return -1;
}
//get eth1 mac addr
memset(hwaddr, 0, 6);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name, 6);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
perror("get_hwaddr ioctl:");
close(sock);
return -1;
} else {
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
}
memset(memzero, 0, 6);
if(memcmp(memzero, hwaddr, 6) == 0){
printf("no mac\n");
return -1;
}
close(sock);
return 0;
}
void printhex(void *hex, int len, char *tag)
{
int i;
unsigned char *p = (unsigned char *)hex;
if(len < 1)
return;
for(i = 0; i < len - 1; i++){
if(*p < 0x10)
printf("0%x%s", *p++, tag);
else
printf("%2x%s", *p++, tag);
}
if(*p < 0x10)
printf("0%x\n", *p++);
else
printf("%2x\n", *p++);
}
#if 0
int rawSocket_sendfile()
{
int i;
unsigned char hwaddr[SRCMAC_LEN];
unsigned char hwaddr_dst[DSTMAC_LEN] = {0x54,0x6C,0xEB,0x2D,0x3D,0xC2};
char devname[3][7];
int ret;
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
struct sockaddr_ll sll;
memset(&sll,0,sizeof(sll));
sll.sll_family = AF_PACKET;//填写AF_PACKET,不再经协议层处理
sll.sll_protocol = htons(ETH_P_ALL);
sll.sll_ifindex = 2; //网卡eth0的index,非常重要,系统把数据往哪张网卡上发,就靠这个标识
sll.sll_pkttype = PACKET_OUTGOING; //标识包的类型为发出去的包
// 以下配置非必须
sll.sll_halen = DSTMAC_LEN; //目标MAC地址长度为6
//填写目标MAC地址
sll.sll_addr[0] = 0x54;
sll.sll_addr[1] = 0x6C;
sll.sll_addr[2] = 0xEB;
sll.sll_addr[3] = 0x2D;
sll.sll_addr[4] = 0x3D;
sll.sll_addr[5] = 0xC2;
long bufSize = 0;
FILE* readfile = fopen("/home/user/Desktop/codes/my_em/my-embedded/app/NET/RawSocket/filename.txt","rb");
if(!readfile)
{
perror("file open failed:\n");
return -1;
}
// get file size
fseek(readfile,0,SEEK_END); // 将文件位置指针移动到最后
bufSize = ftell(readfile); // ftell(FILE *stream):返回给定流stream的当前文件位置,获取当前位置相对文件首的位移,位移值等于文件所含字节数
fseek(readfile,0,SEEK_SET); // 将文件位置指针移动到开头
printf("filesize:%ld\n",bufSize);
// unsigned char buffer[1024] = {0};
unsigned char *buffer = (unsigned char*)malloc(sizeof(unsigned char)*bufSize+8);
if(!buffer)
{
return -1;
}
// dst mac:6 src mac:6
memcpy(buffer,hwaddr_dst,DSTMAC_LEN); // dst mac
memcpy(buffer+DSTMAC_LEN,hwaddr,SRCMAC_LEN); // src mac
memcpy(buffer+DSTMAC_LEN+SRCMAC_LEN,packet_type,PROTOTYPE_LEN); // proto type
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
int nCount;
while((nCount = fread(&buffer[DSTMAC_LEN+SRCMAC_LEN+PROTOTYPE_LEN],1,bufSize,readfile)) > 0)
{
if (sendto(sock, buffer, sizeof(unsigned char)*bufSize+8, 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
printf("Sendto Success!\n");
}
free(buffer);
return 0;
}
#endif
int main()
{
int i,ret;
char devname[3][7];
unsigned char hwaddr[SRCMAC_LEN];
unsigned char buf[1024] = {0};
// 1.get host mac addr
read_netdev_proc(devname, 3, 7);
for(i = 0; i < 3 && get_hwaddr(devname[i], hwaddr) != 0; i++){
//empty
}
printf("devname: [ %s ]\t", devname[i]);
printhex(hwaddr, 6, ":");
// 2.init socket & sockaddr_ll
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sock < 0){
perror("sock");
return -1;
}
struct sockaddr_ll sll;
memset(&sll,0,sizeof(sll));
sll.sll_family = AF_PACKET;//填写AF_PACKET,不再经协议层处理
sll.sll_protocol = htons(ETH_P_ALL);
sll.sll_ifindex = 2; //网卡eth0的index,非常重要,系统把数据往哪张网卡上发,就靠这个标识
sll.sll_pkttype = PACKET_OUTGOING; //标识包的类型为发出去的包
// 以下配置非必须
sll.sll_halen = DSTMAC_LEN; //目标MAC地址长度为6
//填写目标MAC地址
sll.sll_addr[0] = 0x54;
sll.sll_addr[1] = 0x6C;
sll.sll_addr[2] = 0xEB;
sll.sll_addr[3] = 0x2D;
sll.sll_addr[4] = 0x3D;
sll.sll_addr[5] = 0xC2;
// 3.组包
unsigned char packet_start[]={
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,//dst mac
0x00, 0x23, 0x54, 0x0e, 0xe5, 0xd8,//src mac
0x88, 0x8e, //Type: 802.1x authentication
0x01, //Version:v1
0x01, //Type: Start (1)
0x00, 0x00//Length 0
};
unsigned char packet_type[]={
0xff, 0x8e, //Type: 802.1x authentication
};
unsigned char packet_type1[]={
0x08, 0x06, //Type: 802.1x authentication
};
// unsigned char hwaddr_dst[DSTMAC_LEN] = {0x54,0x6C,0xEB,0x2D,0x3D,0xC2};
// unsigned char hwaddr_dst[DSTMAC_LEN] = {0xd8,0xcb,0x8a,0xa3,0x7D,0xCf};
unsigned char hwaddr_dst[DSTMAC_LEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
#if 0
if (sendto(sock, packet_start, sizeof(packet_start), 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
#endif
while(1)
{
// 4.接收rawsocket数据
ret = recvfrom(sock, buf, 1024, 0, NULL, NULL);
// 5.检测过滤第12(根据需求修改)个字节
if(0 == memcmp(packet_type1,&buf[12],2))
{
printf("same recv: ");
printhex(buf, ret, " ");
if(ret == 0)
{
printf("server offline\n");
}
else if(ret < 0)
{
printf("recv");
return -1;
}
// dst mac:6 src mac:6
memcpy(buf,hwaddr_dst,DSTMAC_LEN); // dst mac
memcpy(buf+DSTMAC_LEN,hwaddr,SRCMAC_LEN); // src mac
memcpy(buf+DSTMAC_LEN+SRCMAC_LEN,packet_type,PROTOTYPE_LEN); // proto type
printf("sizeof buf :%d\n",sizeof(buf));
// 6.更新过数据后,发出
if (sendto(sock, buf, sizeof(buf), 0, &sll, sizeof(sll)) < 0){
perror("sendto");
return 1;
}
}
else
{
// printf("recv: ");
// printhex(buf, ret, " ");
// 6.更新过数据后,发出
//if (sendto(sock, buf, sizeof(buf)+8, 0, &sll, sizeof(sll)) < 0){
// perror("sendto");
// return 1;
// }
continue;
}
// sleep(3);
}
}
rawsocket_sendrecv.h
#ifndef _RAWSOCKET_H_
#define _RAWSOCKET_H_
//#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
//#include <arpa/inet.h>
#include <features.h> /* for the glibc version number */
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#include <stdio.h>
//#include <netinet/in.h>
//#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <linux/if_packet.h>
#include <linux/if.h>
#define SRCMAC_LEN 6
#define DSTMAC_LEN 6
#define PROTOTYPE_LEN 2
#define _PATH_PROCNET_DEV "/proc/net/dev"
int get_hwaddr(char *name, unsigned char *hwaddrr);
char *get_name(char *name, char *p) ;
int read_netdev_proc(void *devname, const int num, const int len);
void printhex(void *hex, int len, char *tag) ;
int rawSocket_sendata(int sock,unsigned char* buffer,int len);
int rawSocket_recvdata(int sock,unsigned char* buf);
#endif
参考文章:

浙公网安备 33010602011771号