【STM32】 TCP/IP通信协议(3)--LwIP网络接口

LwIP协议栈支持多种不同的网络接口(网卡),由于网卡是直接跟硬件平台打交道,硬件不同则处理也是不同。那Iwip如何兼容这些不同的网卡呢?

LwIP提供统一的接口,底层函数需要用户自行完成,例如网卡的初始化、接收和发送数据等。当LwIP底层接收到网络数据时,需要经过层层递交才会传入内核处理;相反,LwIP发送数据时也会调用网卡的发送数据。对于没有接触过LwIP的人新手来说,我们如何编写这个底层代码呢?不用担心,LwIP提供了一个ethernetif.c 文件作为底层接口的驱动模版。用户只根据自己的网络设备进行参照修改即可。

在LwIP中,netif数据结构用于描述一个网络接口的特性。这个结构体是在netif.h文件中定义的

struct netif {
 /* 指向下一个 netif 结构的指针 */
 struct netif *next;
 
 /* IP 地址相关配置 */
 ip_addr_t ip_addr; /* 网络接口的 IP 地址 */
 ip_addr_t netmask; /* 子网掩码 */
 ip_addr_t gw; /* 网关地址 */
 
 /* 该函数向 IP 层输入数据包 */
 netif_input_fn input;
 
 /* 该函数发送 IP 包 */
 netif_output_fn output;
 
 /* 该函数实现底层数据包发送 */
 netif_linkoutput_fn linkoutput;
 
 /* 该字段用户可以自由设置,例如用于指向一些底层设备相关的信息 */
 void *state;
void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + 
LWIP_NUM_NETIF_CLIENT_DATA];
​
 /* 该接口允许的最大数据包长度 */
 u16_t mtu;
 
 /* 该接口物理地址长度 */
 u8_t hwaddr_len;
 
 /* 该接口的物理地址 */
 u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
 
 /* 该接口的状态、属性字段 */
 u8_t flags;
 
 /* 该接口的名字 */
 char name[2];
 
 /* 接口的编号 */
 u8_t num;
 
 /* 需要发送的路由器请求消息的数量 */
 u8_t rs_count;
};

这些字段用于描述各个网卡的差异,每个网卡都通过一个 netif 结构体进行抽象表示。多 个网卡对应多个 netif,这些 netif 以链表的形式链接起来,形成一个单向链表。

这个链表的首个节点由 netif_list 指针指向,lwIP 内核使用该指针遍历和查询 netif 链表。

struct netif *netif_list; /* 网络接口链表指针 */
struct netif *netif_default; /* 哪个网络接口(多网口时候) */
static u8_t netif_num; /* 为网口分配唯一标识 */

在 lwIP 中,netif.c 文件包含了管理网络接口的核心函数。其中,netif_default 指针指向默 认的网络接口,当网络层需要发送数据包时,系统优先选择该指针所指向的网卡进行数据发送。 如果该网卡没有响应,则会选择其他网卡进行发送

1.netif_add 函数

该函数用于将新创建的 netif 插入到 netiflist 队列中,以表示添加了一个网络接口。

struct netif *
netif_add( struct netif *netif,
 const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
const ip4_addr_t *gw,
 void *state, netif_init_fn init, netif_input_fn input)
{
    /* 清空主机 IP 地址、子网掩码、网关等信息。 */
    ip_addr_set_zero_ip4(&netif->ip_addr);
     ip_addr_set_zero_ip4(&netif->netmask);
     ip_addr_set_zero_ip4(&netif->gw);
    netif->output = netif_null_output_ip4;
 
    /* 传输的最大数据长度 */
    netif->mtu = 0;
 
    /* 网络的接口状态 */
     netif->flags = 0;
    memset(netif->client_data, 0, sizeof(netif->client_data));
 
    /* 传递进来的参数填写网卡 state、input 等字段的相关信息 */
     netif->state = state;
 
     /* 并为当前网卡 分配唯一标识 num */
     netif->num = netif_num;
 
     /* 网卡输入 */
     netif->input = input;
 
     /* 调用网卡设置函数 netif_set_addr()设置网卡 IP 地址、子网掩码、网关 */
    netif_set_addr(netif, ipaddr, netmask, gw);
 
     /* 为 netif 调用用户指定的初始化函数 */
     if (init(netif) != ERR_OK) {
         return NULL;
     }
 
     /* 将这个 netif 添加到列表中 */
     netif->next = netif_list;
     netif_list = netif;
    mib2_netif_added(netif);
    netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
    
     return netif;
}

每一个 netif 结构体是对一个网卡进行抽象,包含了该网 卡的收发函数、状态等信息。

注:新插入的 netif 结构体是在 netiflist 队列的首部插入

2.netif_set_default 函数

该函数用于将指定的 netif 结构体设置为默认网卡,使 lwIP 内核优先对该网卡进行操作。

void
netif_set_default(struct netif *netif)
{
    if (netif == NULL)
    {
        /* 删除默认路由 */
        mib2_remove_route_ip4(1, netif);
    }
    else
    {
        /* 添加默认路由 */
        mib2_add_route_ip4(1, netif);
    }
    netif_default = netif; /* 选择那个网络接口 */
}

/*********************怎么使用函数 netif_set_default()*********************/
/* 通过该函数,将网络接口添加到链表中 */
netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init,&tcpip_input);

/* 注册默认的网络接口 */
netif_set_default(&xnetif);

调用该函数,可以设置指定的网络接口为默认网卡。在函数内部,会更新相应的默认 网卡指针,并确保后续的网络操作优先使用该网卡进行数据传输。

相关推荐
拾木2004 小时前
网络协议 TCP、UDP 和 HTTP
网络协议·tcp/ip·udp
hgdlip4 小时前
笔记本电脑如何改ip地址:操作指南与注意事项
网络协议·tcp/ip·电脑
wangqiaowq6 小时前
Ingress Gateway 它负责处理进入集群的 HTTP 和 TCP 流量
tcp/ip·http·gateway
中云时代-防御可测试-小余6 小时前
国产游戏出海火热另一面:AI和API快速成长引发网络安全挑战
人工智能·tcp/ip·web安全·游戏·网络安全·udp·ddos
TechubNews6 小时前
解读 Story Protocol:IP 与区块链的潜力与障碍
人工智能·tcp/ip·区块链
KookeeyLena76 小时前
广播IP与共享IP的关系
服务器·网络·tcp/ip
程序员奇奥6 小时前
计算机网络:三次握手和四次挥手详解
网络·tcp/ip·计算机网络
沥川同学7 小时前
计算机网络自顶向下(2)----socket编程
linux·网络·websocket·tcp/ip·计算机网络·udp
群联云防护小杜9 小时前
解决数藏平台中因用户使用科技脚本而导致服务器卡顿的方法
运维·服务器·网络·科技·tcp/ip·区块链