前言
李柱明博客:[https://www.cnblogs.com/lizhuming/p/15487068.html](https://www.cnblogs.com/lizhuming/p/15487068.html)
3. 网络接口
3.1 概念引入
网络接口(以太网接口)是硬件接口(网络接口又可以称之为网卡)。
LWIP 是软件那么而怎样让硬件和软件无缝连接起来呢?
而且网卡又多种多样,怎样才能让 LWIP 使用同样的软件兼容不同的硬件平台?
LWIP 中使用了一个netif
结构体来描述网卡但是网卡是直接和硬件平台打交道的:
-
用户提供最底层接口函数。
-
LWIP 提供统一的 API。
-
举例:
- 收:如网卡的初始化和网卡的收发数据,当 LWIP 底层得到数据之后,才会传入到内核中去处理。
- 发:LWIP 内核需要发送数据包的时候,也需要调用网卡的发送函数。
-
LWIP 中的 etherneif.c 文件的函数通常为硬件打交道的底层函数,当有数据需要通过网卡接收或者发送数据的时候就会被调用,通过 LWIP 的协议栈的内部进行处理后,从应用层就能得到数据或者可以发送数据。
小总结:
简单来说,netif 是 LWIP 抽象出来的网卡,LWIP 协议栈可以使用多种不同接口,而 etherneif.c 文件则提供了 netif 访问各种不同的网卡,每个网卡有不同的实方式,每户只需要修改 ethernetif.c 文件即可。
3.2 lwip netif 结构体
3.2.1 链接
源码:((20210727163318-nobqihq))
3.2.2 字段分析
3.2.3 网卡链表
/** pointer to next in linked list */
struct netif *next;
LWIP 使用链表来统一管理同一设备的多个网卡。
netif.c 文件中定义两个全局指针 struct netif *netif_list
和 struct netif *netif_default
netif_list
就是网卡链表指针,指向网卡链表的首节点(第一个网卡)。netif_default
默认网卡。
3.2.4 网络 IP
#if LWIP_IPV4
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
#endif /* LWIP_IPV4 */
ip_addr
:网络中的 IP 地址。
netmask
:子网掩码。
gw
:网关地址。
3.2.5 接收数据函数
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
该函数由网络设备驱动程序调用,将数据包传递到 TCP/IP 协议栈(IP 层),这通常是 ethernet_input()
,参数为 pbuf
和 netif
类型,其中 pbuf
为接收到的数据包。
3.2.6 发送数据函数
#if LWIP_IPV4
/** This function is called by the IP module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet.
* For ethernet physical layer, this is usually etharp_output() */
netif_output_fn output;
#endif /* LWIP_IPV4 */
函数由 IP 层调用,在接口上发送数据包。用户需要编写该函数并使 output 指向它。
通这个函数的处理步骤是首先解析硬件地址,然后发送数据包。
对于以太网物理层,该函数通常是 etharp_output()
,参数是 pbuf
,netif
,和 ip_addr
类型。
注意:其中 ipaddr
代表要将数据包发送到的地址,但不一定数据包最终到达的 ip 地址。比如要发送 ip 数据报到一个并不在本网络的主机上,该数据包要被发送到一个路由器上,这里的 ipaddr 就是路由器的 ip 地址。
3.2.6 ARP 模块调用的发送函数
/** This function is called by ethernet_output() when it wants
* to send a packet on the interface. This function outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;
该函数和 output
类似,也需要用户自己实现一个函数,但是只有两个参数,它是由 ARP 模块调用的,一般是自定义函数 low_level_output()
。
当需要在网卡上发送一个数据包时,该函数会被 ethernet_output()
函数调用。
3.2.7 出口回调函数
#if LWIP_NETIF_REMOVE_CALLBACK
/** This function is called when the netif has been removed */
netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
当 netif 被删除时调用此函数。
3.2.8 用户私有数据
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
#ifdef netif_get_client_data
void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_IPV6_AUTOCONFIG
/** is this netif enabled for IPv6 autoconfiguration */
u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/** Number of Router Solicitation messages that remain to be sent. */
u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if LWIP_NETIF_HOSTNAME
/* the hostname for this netif, NULL is a valid value */
const char* hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
由设备驱动程序设置并指向设备的状态信息,主要将网卡的某些私有数据传递给上层。
3.2.9 最大传输单位
/** maximum transfer unit (in bytes) */
u16_t mtu;
3.2.10 链路硬件地址长度&地址
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
3.2.11 网卡信息状态标志
/** flags (@see @ref netif_flags) */
u8_t flags;
网卡状态信息标志位,是很重要的控制字段,它包括网卡的功能使能,广播使能,ARP 使能等重要控制位。
3.2.12 网卡名字
/** descriptive abbreviation */
char name[2];
用于保存每一个网卡的名字,用两个字符的名字来标识。
网卡使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡表示的硬件的种类。
如果两个网卡具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡。
3.2.13 网卡标识
/** number of this interface */
u8_t num;
标识使用同种驱动类型的不同网卡。
3.3 netif 使用
简要步骤:
-
定义一个
netif
作为网卡设备结构体。 -
挂载到
netif_list
链表中。- 使用函数:
netif_add();
- 建议参考该函数源码。
- 使用函数:
3.4 与 netif 相关的底层函数
主要 API:
/* 网卡初始化函数 */
static void low_level_init(struct netif *netif);
/* 网卡的发送函数,
* 将内核的数据包发送出去,数据包采用pbuf数据结构进行描述 */
static err_t low_level_output(struct netif *netif, struct pbuf *p);
/* 网卡的数据接收函数,
* 该函数必须将接收的数据封装成pbuf的形式 */
static struct pbuf * low_level_input(struct netif *netif);
相关 API:
/* 上层管理网卡netif的到时候会被调用的函数,
* 最终还是调用 low_level_init() 函数 */
err_t ethernetif_init(struct netif *netif);
/* 主要作用就是调用low_level_input()函数从网卡中读取一个数据包,
* 然后解析该数据包的类型是属于ARP数据包还是IP数据包,
* 再将包递交给上层。
* 无操作系统中使用:可以直接使用的函数,因为内核会周期性去处理该接收函数。
* 有操作系统中系统:一般会将其改写成一个线程的形式,可以周期性去调用low_level_input()网卡接收函数。*/
void ethernetif_input(void *pParams);
附件-源码
struct netif
/** Generic data structure used for all lwIP network interfaces.
* The following fields should be filled in by the initialization
* function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
struct netif {
/** pointer to next in linked list */
struct netif *next;
#if LWIP_IPV4
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
/** Array of IPv6 addresses for this netif. */
ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
/** The state of each IPv6 address (Tentative, Preferred, etc).
* @see ip6_addr.h */
u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
#endif /* LWIP_IPV6 */
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
#if LWIP_IPV4
/** This function is called by the IP module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet.
* For ethernet physical layer, this is usually etharp_output() */
netif_output_fn output;
#endif /* LWIP_IPV4 */
/** This function is called by ethernet_output() when it wants
* to send a packet on the interface. This function outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;
#if LWIP_IPV6
/** This function is called by the IPv6 module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet.
* For ethernet physical layer, this is usually ethip6_output() */
netif_output_ip6_fn output_ip6;
#endif /* LWIP_IPV6 */
#if LWIP_NETIF_STATUS_CALLBACK
/** This function is called when the netif state is set to up or down
*/
netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
/** This function is called when the netif link is set to up or down
*/
netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
/** This function is called when the netif has been removed */
netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
#ifdef netif_get_client_data
void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_IPV6_AUTOCONFIG
/** is this netif enabled for IPv6 autoconfiguration */
u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/** Number of Router Solicitation messages that remain to be sent. */
u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if LWIP_NETIF_HOSTNAME
/* the hostname for this netif, NULL is a valid value */
const char* hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
/** maximum transfer unit (in bytes) */
u16_t mtu;
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
/** flags (@see @ref netif_flags) */
u8_t flags;
/** descriptive abbreviation */
char name[2];
/** number of this interface */
u8_t num;
#if MIB2_STATS
/** link type (from "snmp_ifType" enum from snmp_mib2.h) */
u8_t link_type;
/** (estimate) link speed */
u32_t link_speed;
/** timestamp at last change made (up/down) */
u32_t ts;
/** counters */
struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP
/** This function could be called to add or delete an entry in the multicast
filter table of the ethernet MAC.*/
netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IPV4 && LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
/** This function could be called to add or delete an entry in the IPv6 multicast
filter table of the ethernet MAC. */
netif_mld_mac_filter_fn mld_mac_filter;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if LWIP_NETIF_HWADDRHINT
u8_t *addr_hint;
#endif /* LWIP_NETIF_HWADDRHINT */
#if ENABLE_LOOPBACK
/* List of packets to be queued for ourselves. */
struct pbuf *loop_first;
struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFS
u16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */
};