您的位置  > 互联网

网卡的I/O配置如何在自制操作系统写网卡驱动程序

30天自制操作系统写网卡驱动[4]:寄存器控制网卡发送和接收数据

30天自制操作系统写网卡驱动之《3》:读取网卡MAC地址

在自制操作系统上编写网卡驱动(二):网卡的I/O配置

如何在自制操作系统中编写网卡驱动(一)

今天的:

事实上,我们可以编写自己独特的协议。 根据UDP协议在数据中添加端口号。 根据IP协议将IP地址添加到数据中。 使用以太网协议指定阵列的网卡地址。 编写代码生成 DHCP 信息并发送。 使用抓包软件监控发送过程。

上一篇文章我们已经完成了网卡8029的发送控制,可以发送8029的数据了。

然而,为了使发送的数组S[60]能够与网络上的其他网卡进行通信,必须根据一些特定的协议来组织数组S[60]。

网卡还没有分配IP地址,所以我们按照DHCP协议来组织阵列,希望路由器能够按照DHCP协议为我们分配IP地址。

经过[4]的努力,我们生成了一个DHCP数组,然后为了给数组添加端口号,我们按照UDP协议在数组中添加了一个8字节长度的UDP。

事实上,我们可以编写自己独特的协议

然而,现在阵列被发送到网络,没有网卡会接收它。

因为虽然使用UDP协议已经将端口号包含在数组中,但是还没有指定接收者的IP地址和MAC地址,所以没有网卡会解释这个数组。 因为网卡是按照协议来解释数据的,所以当网卡收到数据后,会检查MAC地址,发现不是自己的MAC地址,所以就不会继续解释数据了。

从这里我们也明白了,网卡其实可以接收网络上的任何数据,但是这个数据是否需要分析解读,就取决于数据中是否包含我们想要的信息。

因此,只要有一张网卡,我们就可以开发一款可以接收网络上任何数据的网卡。

也就是说,我们其实可以使用UDP、IP、TCP、以太网等协议。 我们可以自己设计一些协议来传输我们自己的信息。

然而,这些是现在公开的统一协议。 比如我买的路由器自带的协议是DHCP来分配IP地址。 因此,我只能按照DHCP协议指定的方式来制作数组。

请记住:协议是由人制定的。 只要发送方和接收方知道协议中每个数字的含义,就可以完成信息的交换。 如果为了完成加密,任何现有的协议都可以使用。

比如我可以自己设置一些协议,专门用于自制操作系统之间的信息交换。

但由于网卡不是我们生产的,因此网卡传输的数据仍然必须按照以太网协议规定的方式组织。

因此,向阵列添加端口号、IP地址和MAC地址必须遵守一些现有协议。

按照UDP协议规定的方法添加端口号。

添加IP地址时,必须按照IP协议规定的方法添加。

添加MAC地址时,必须按照以太网规定的方式添加。

根据UDP协议为数据添加端口号

在[4]中,我们按照UDP协议规定的方法在DHCP数组前面添加了8个字节:0, 0x44, 0, 0x43, 0x1, 0x2c, 0, 0x80。

其中0、0x44,两个字节,对应十进制的68,表示我们的出端口是68。

其中0、0x43是两个字节,对应十进制的67,表示该报文需要由路由器上67端口的程序处理。

接下来的四个字节是数据的长度(8 + DHCP 的字节数)和校验和。

编写具体代码时,只需要编写一个8字节的结构体,然后给这个结构体赋值即可:

// 定义UDP header的结构体
typedef struct{
	short src_port;
	short dest_port;
	short length;
	short check_sum;
} UDP_HEADER;

新增加的8个字节称为UDP。

UDP 放在 DHCP 数据前面,得到的总数组称为 UDP 数组。

//定义UDP数组对应的结构体,可以看到,UDP数组的前8个字节是UDP header
struct UDP_PACKAGE{
	UDP_HEADER header;
	DHCP_MESSAGE message; // 这就是我们的DHCP discover数组
};
// 定义出DHCP discover数组的结构体
typedef struct{
    unsigned char op;
    unsigned char htype;
    unsigned char hlen;
    unsigned char hops;
    long xid;
    short secs;
    short flag;
    long ciaddr;
    long yiaddr;
    long siaddr;
    long giaddr;
    unsigned char chaddr[16];
    unsigned char sname[64];
    unsigned char file[128];
    unsigned char magic_cookie[4];
    unsigned char options[44];
    unsigned char end[10];
} DHCP_MESSAGE;

那么今天我们继续按照IP协议向阵列添加IP地址,按照以太网协议向阵列添加MAC地址。

根据IP协议为数据添加IP地址

根据IP协议,只需在UDP数组前添加如下字段数据即可:

IP协议指定要添加的字段

这些字段称为 IP。 我们先按照这个写一个结构:

// IP header对应的结构体
typedef struct {
	unsigned char ip_and_headlength;
	unsigned char service_level;
	short length;
	short identify;
	short label_and_offset;
	unsigned char    ttl;
	unsigned char protocal;
	short head_checksum;
	long src_ip;
	long dest_ip;
} IP_HEADER;
// 将IP header 放在UPD 数组前,构成IP数组
typedef struct {
	IP_HEADER header;
	struct UDP_PACKAGE udp;
} IP_PACKAGE;

然后编写一个函数给IP赋值:

void init_ip_package(IP_HEADER * header){
    /*
    unsigned char ip_and_headlength;
    unsigned char service_level;
    short length;
    short identify;
    short label_and_offset;
    unsigned char    ttl;
    unsigned char protocal;
    short head_checksum;
    long src_ip;
    long dst_ip;
    struct UDP_PACKAGE udp;
    */
    header->ip_and_headlength=0x45;
    header->service_level=0x00;
    //header->length=(sizeof(IP_PACKAGE)&0xff00)>>8 + (sizeof(IP_PACKAGE)&0xff)<<8;
    header->length=hxl(sizeof(IP_PACKAGE)-4);
    //header->length=sizeof(IP_PACKAGE)-4;
    header->identify=0x3333;
    header->label_and_offset=hxl(0x1<<14);// 不分段
    header->ttl=0x80;
    header->protocal=17;// 协议号是10进制的
    header->head_checksum=0;
    header->src_ip=0x00000000;
    header->dest_ip=0xffffffff;
}

=0x45,对应图中的第一个字节。 该字节的高4位是IP,即IP协议的版本。 这里,4指的是IPv4版本; 低4位是长度值,但这个长度值只是加上IP的长度值,所以叫is,这个长度值的单位是双字。 一个双字包含4个字节,所以=5,也就是说这个IP的长度是5个双字,也就是5x4=20个字节。

=0x00,对应图中第二个字节,Type of,TOS,字面意思是服务类型。 事实上,这个字段规定了采用哪种策略进行传输,是最小延迟、最高可靠性、最小成本还是最大吞吐量? 我们这里设置为0,即正常传输。 如果您想了解更多详情,可以看这里:

=hxl(()-4),对应图中第3、4个字节,Total,即IP+UDP数组的长度。 为什么我要把这个长度减少 4? 因为我发现构造出来的结构体比原来多了4个零,所以我减去了这四个零。 另外,使用的hxl是这样的:

unsigned short hxl(unsigned short length){
	length=length&0x0000ffff;
	return ((length&0xff00)>>8 | (length&0xff)<<8) &0xffff;
}

通过hxl交换short类型的高8位和低8位。 长度本身就是一个词,即短。 当一个字存入存储器时,先存该字的低8位,后存该字的高8位。 但传输时需要先传输高8位,再传输低8位,所以这里我们先传输高8位。 其实这个变量定义得更合适。

->= 是IP的第5、6个字节,即,或者ID。 字面意思是:身份标识符,或者分段身份标识符。 这个字段是用来表示IP阵列的身份的? IP阵列的身份是什么? IP数组用来传输我们要传输的数据,但是如果我们要传输的数据太长,超过了IP数组可以承载的最大长度,比如字段是,

最大长度是65536,如果我们要传输的数组比这个长,我们还能用IP协议规定的方法给数组添加IP地址吗?

答案是肯定的。

但是IP协议要求我们将数据分段传输,比如分成2段,在每段数据前加上IP,然后分2段发送出去。 那么两次发送的数据也两次收到。 现在问题来了?

接收方如何知道两次收到的数据应该一起使用?

因此,为了让接收方能够将两份数据结合起来,我们需要给每一个发送的数据赋予一个标识:即也可以直接称为ID。

如何赋予这个值? 为什么我们在这里给一个? 因为对数组进行分段后,第一个段的ID是一个随机值,第二个段的ID是第一个段的ID+1。

例如,如果我们想将数组分两部分传输,那么当向数组的第一部分添加IP时,我们随机给1,那么当向数组的第二部分添加IP时,=+1=。

=hxl(0x1