【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 收发实例运行

发送端

image

接收端

image

wireshark抓取报文

image

参考文章:

读取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端:

img

img

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抓取报文情况:

img

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;  
}  


接收存储文件的内容:

img

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

参考文章:

C/C++用socket实现简单的TCP文件传输_c++ socket库-CSDN博客

posted @ 2024-07-17 17:36  Emma1111  阅读(111)  评论(0)    收藏  举报