首页 > 系统相关 >【Linux网络】Linux网络协议栈常用代码片段

【Linux网络】Linux网络协议栈常用代码片段

时间:2024-07-17 17:44:26浏览次数:12  
标签:片段 name int sock 网络协议 char Linux hwaddr include

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

运行结果:

发送端

image

接收端

image

wireshark抓取报文

image

参考文章:

读取linux下的网络设备的mac地址与发送原始数据包_linux检查报文源mac-CSDN博客

UDP协议 sendto 和 recvfrom 浅析与示例 - 阳光长脸兽 - 博客园 (cnblogs.com)

标签:片段,name,int,sock,网络协议,char,Linux,hwaddr,include
From: https://www.cnblogs.com/Wangzx000/p/18292170

相关文章

  • 【Linux网络】Linux网络协议栈问题汇集
    Linux本机与本机socket通信会走网卡吗?1.127.0.0.1本机网络IO需要经过网卡吗?不需要经过网卡,即使把网卡拔了本机网络还是可以正常使用的。本机网络IO的内核执行流程:跨机网络IO的流程:2.数据包在内核中是什么走向,和外网发送相比流程上有什么区别?本机网络IO和跨机IO比较起......
  • Linux配置FTP(vsftpd)
    1、安装vsftpdsudoyuminstall-yvsftpd2、创建虚拟用户数据库#进入vsftpd配置文件cd/etc/vsftpd#创建虚拟用户数据vimvuser.txt#内容如下:vuser1password1vuser2password23、生成虚拟用户数据库db_load-T-thash-f/etc/vsftpd/vusers.txt/etc/vsftp......
  • linux 互斥锁mutex锁使用示例
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、互斥锁mutex是什么?二、代码示例总结前言提示:这里可以添加本文要记录的大概内容:linux互斥锁mutex锁使用示例,两个线程操作一个全局变量。提示:以下是本篇文章正文内容,下面案例可供......
  • linux下使用fdisk进行磁盘分区详解
     转载:https://www.cnblogs.com/renshengdezheli/p/13941563.html目录一.前言二.关于磁盘分区的结构三.fdisk命令详解四.使用fdisk进行磁盘分区4.1磁盘分区规划4.2fdisk进行磁盘分区4.3格式化分区4.4创建挂载点/挂载目录4.5挂载分区4.6设置开机自动挂载分区......
  • 【攻防技术系列+权限维持】Linux隐藏技术
    #权限维持#Linux#隐藏一、隐藏文件二、隐藏文件时间戳三、隐藏权限四、隐藏历史操作五、隐藏端口六、隐藏进程一、隐藏文件创建一个隐藏文件:touch.test.txt一般的Linux下的隐藏目录使用命令ls-l是查看不出来的,只能查看到文件及文件夹,查看Linux下的隐藏......
  • 【Linux杂货铺】期末总结篇4:shell编程
    ......
  • 每天学一个 Linux 命令(18):mv
    命令简介mv命令用于移动并重命名文件和目录。或者将文件从一个目录移动到另一个目录中,如果将一个文件移动到一个已经存在的目标文件中,这时目标文件的内容会被此文件内容覆盖。如果源为文件,而目标为目录,mv将进行文件的位置移动。如果源为目录,则目标只能是目录(不能为文件),mv将进......
  • [定时任务未实现]Linux制定定时任务未生效
    定时任务:$crontab-l#deletearchivelogbefore7days0002***sh/home/oracle/tkdba/del_arch.sh>>/home/oracle/tkdba/logs/del_arch.log.$(date+%F)2>&1观察定时任务没有执行,临时文件没有清理。通过分析日志/var/log/cron,这里$(date+%F)未执行。Jul1523:30......
  • Linux 提权-密码搜寻
    本文通过Google翻译PasswordHunting–LinuxPrivilegeEscalation这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。导航0前言1密码搜寻–文件名和文件内容1.1寻找有趣的文件名1.2寻找有趣的字符串2密码搜寻–Web文......
  • Linux 查看 && 修改端口范围限制
    1.显示当前临时端口的范围:sysctlnet.ipv4.ip_local_port_range或 cat/proc/sys/net/ipv4/ip_local_port_range一般情形下:linux临时端口号范围是(32768,61000)2.暂时性修改临时端口的范围:#echo102465535>/proc/sys/net/ipv4/ip_local_port_range或者 sudosysctl-wne......