一、网络接口层简介
前面提到TCP/IP可分为四层,最底层是网络接口层,实际可细分为物理层和逻辑链路层,逻辑链路层又可细分为介质访问控制子层(即MAC层)与逻辑链路控制子层(即LLC层),大致关系如下:
1.1 物理层
物理层通过把上层的比特流(0/1二进制流)转换为电压的高低、灯光的亮灭等物理信号将数据传输出去,接收端收到这些物理信号后再将这些电压的高低、灯光的亮灭信号恢复为比特流。因此,物理层的规范中包括比特流的转换规则和传输介质两部分。
物理层传输介质大体可分为有线传输介质与无线传输介质两大类:有线传输介质有同轴电缆、双绞线、光纤电缆等;无线传输介质有蓝牙、Wi-Fi、红外线IrDA、移动通信等。
物理层比特流的转换规则就是数据编解码方法,根据信号类型也可分为模拟数据编码和数字数据编码两大类:模拟数据编码(常用于无线传输介质)主要分为振幅键控(ASK)、移频键控(FSK)、移相键控(PSK)等;数字数据编码(常用于有线传输介质)主要分为非归零码NRZ、曼切斯特编码、差分曼切斯特编码等。编码图示如下:
1.2 逻辑链路控制层
MAC子层的主要作用也可分为两部分:一部分面向上层LLC负责把物理层的0/1比特流组建成数据帧或把数据帧分解为0/1比特流(数据帧的封装与卸装),包括数据帧的寻址、识别、发送、接收、差错控制等;另一部分面向下层PHY提供对共享介质的访问方法,包括以太网的带冲突检测的载波侦听多路访问(CSMA/CD)、令牌环(TokenRing)、光纤分布式数据接口(FDDI)等。
MAC子层对介质的访问控制主要包括介质分配(避免碰撞)和竞争裁决(碰撞处理),对传输介质的访问可大体分为共享介质和非共享介质两类:共享介质指多个设备共享一个通信介质,包括蓝牙/WIFI使用的无线信道,为了避免多个设备同时发送数据帧引起冲突,常在发送数据帧前检测通信介质上的数据流动情况,一般为半双工通信;非共享介质主要是每个设备直连交换机,由交换机负责转发数据帧,每个设备与交换机之间的通信介质是专用的,这种情况不需要进行冲突检测,可实现全双工通信。
MAC子层管理的数据帧为了便于寻址,包含物理地址字段(也叫MAC地址),数据帧也正是靠源MAC地址与目的MAC地址实现数据帧的寻址(仅限于同一数据链路段,可以跨交换机、网桥、中继器等链路层中转设备,但不能跨路由器等网络层中转设备,跨越路由器的寻址靠IP地址完成)、发送与接收的。MAC子层将目标计算机的物理地址添加到数据帧上,当此数据帧传递到对端的MAC子层后,它检查该地址是否与自己的地址相匹配,如果帧中的地址与自己的地址不匹配,就将这一帧抛弃;如果相匹配,就将它发送到上一层中。在有线介质比如以太网或FDDI中根据IEEE802.3的规范使用MAC地址,在无线介质比如Wi-Fi(IEEE802.11)、蓝牙(IEEE802.15)等设备中也使用相同规范的MAC地址,所以MAC子层的存在屏蔽了不同物理链路种类的差异性。
MAC地址长48比特,在使用网卡(NIC)的情况下,MAC地址一般会被烧入到ROM中。因此,任何一个网卡的MAC地址都是唯一的,在全世界都不会重复(实际上有例外情况,比如虚拟MAC地址可能有重复,重复的MAC地址只要不是同属于一个数据链路就不会出现问题,一个主机是靠IP地址与MAC地址共同唯一确定的)。MAC地址中3~24位表示厂商识别码OUI(Organizationally unique identifier),每个NIC厂商都有特定唯一的识别数字,后24位是厂商内部为识别每个网卡而用的,前两位则跟MAC地址类型有关,MAC地址的结构如下图示:
MAC地址的源地址就是真实的MAC地址(属于单播地址),目的地址则可分为三类:单播地址、多播地址和广播地址。单播地址通常与一个网卡的具体地址相对应,它要求第一个字节的bit0(即最先发出去的位)必须为0;多播地址则要求第一个字节的bit0为1,在网络中多播地址不会与任何网卡的MAC地址相同,而多播数据可以被多个网卡同时接收;广播地址的所有48位全为1(即FF-FF-FF-FF-FF-FF),同一链路层局域网中的所有网卡都可以接收到广播数据包。
LLC子层向高层提供一个或多个逻辑接口,这些接口被称为服务访问点(SAP),多个逻辑接口可能共用一个物理接口,每个逻辑接口可能通过SAP服务一个上层协议。LLC子层将物理链路抽象为逻辑链路,基本与物理介质完全无关了,屏蔽了不同MAC子层之间的差异,对上层网络层来的不同协议进行翻译和控制,并向下传递同样的数据帧,以使其可以在物理层传送。相对于有线传输的线缆直连,无线传输的链路管理更为复杂,需要将电磁波资源划分为不同的信道,无线链路的建立、加密、认证、释放等也是由LLC规定的,读者感兴趣可以详细了解下IEEE802.11(Wi-Fi)与IEEE802.15(BlueTooth)的规范,本系列主要介绍更上层的协议栈,就以IEEE802.3以太网规范为例了。下面列出部分数据链路名及其主要参数供参考:
1.3 以太网数据帧简介
目前有线传输使用较广的是以太网,下面先介绍下以太网的数据帧构成如下:
现在使用比较多的以太网帧是前者,跟IEEE802.3定义的以太网帧略有差别,两者前面都包含目标MAC地址与源MAC地址字段,用于数据帧的寻址、发送、接收等,最后都有FCS(Frame Check Sequence)用于差错校验。不同的是源MAC地址后与数据前的部分,以太网帧的类型字段用于标识上层协议类型,而IEEE802.3帧的帧长度字段不足以标识上层协议类型,需要靠LLC/SNAP(SubNetwork Access Protocol)字段标识出上层协议类型信息。常见的协议类型编号如下:
以太网帧前端还有一个叫做前导码的部分,它由0/1交替组合而成,表示一个以太网帧的开始,也是为了实现物理层数据的正确传输,物理层使用7个字节的前同步码实现物理层帧输入/输出的同步,使用1个字节的SFD(Start Frame Delimiter帧首定界符,末尾是11)标识帧的开始,共8字节的前导码结构如下:
以太网的数据帧相对比较简单,基本省去了LLC子层的部分,Wi-Fi/蓝牙的链路层数据帧就比较复杂了,专门介绍蓝牙/Wi-Fi时再详细介绍。
链路层数据帧的封装属于网卡驱动的范畴,并不算TCP/IP协议栈的部分,这里就不详细介绍链路层数据帧的数据结构和操作函数的实现了。下面介绍TCP/IP协议栈对不同网卡抽象出的网络接口如何管理,由于从一台主机寻址到另一台主机,需要IP地址进行网际寻址、MAC地址进行局域网内寻址,所以每个网络接口都会有一个IP地址和一个MAC地址,具体寻址过程下一篇介绍。
二、网络接口管理
网络接口层旨在对具体网络硬件、软件进行统一的封装,并为协议栈上层(IP层)提供统一的接口服务。在LwIP运行的目标系统上可能存在多个网络接口,比如可能有多个网卡,也可能有串行网络接口(串口),还可能有环回接口(提供了一种对硬件接口的纯软件模拟,允许用户在没有硬件网络接口的环境下运行并调试协议栈,也可用于进程间通信)。
为了实现对这些接口结构的有效管理,LwIP会为每个接口分配一个netif结构,用这个结构来描述每种接口的特性,如接口IP、接口状态等,同时在该结构中为每个接口注册对应的操作函数,如数据包输入函数、输出函数等。内核将所有网络接口的netif结构组织在一个叫做netif_list的链表上,当有IP数据包需要发送时,IP层会根据数据包的目的IP地址,在netif_list链表中选择一个最合适的网络接口,并调用其注册的数据包发送函数将数据包发送出去;当网卡接收到数据包时,其注册的数据包输入函数会被调用,完成将数据包递交给IP层的任务。从整个过程来看,网络接口管理有效地为上层屏蔽掉底层各个硬件接口间的差异,并为底层网络接口驱动程序的编写提供了规范化的接口定义。
2.1 网络接口的描述
数据结构netif是协议栈内核的重要组成部分,它完成了对各种类型网络接口的抽象。下面先看下该数据结构的实现代码:
// rt-thread\components\net\lwip-1.4.1\src\include\lwip\netif.h
/** 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;
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
/** 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. */
netif_output_fn output;
/** This function is called by the ARP module 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;
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
/** 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 NETIF_FLAG_ below) */
u8_t flags;
/** descriptive abbreviation */
char name[2];
/** number of this interface */
u8_t num;
#if ENABLE_LOOPBACK
/* List of packets to be queued for ourselves. */
struct pbuf *loop_first;
struct pbuf *loop_last;
#endif /* ENABLE_LOOPBACK */
};
/** must be the maximum of all used hardware address lengths
across all types of interfaces in use */
#define NETIF_MAX_HWADDR_LEN 6U
/** Whether the network interface is 'up'. This is
* a software flag used to control whether this network
* interface is enabled and processes traffic.
* It is set by the startup code (for static IP configuration) or
* by dhcp/autoip when an address has been assigned.*/
#define NETIF_FLAG_UP 0x01U
/** If set, the netif has broadcast capability.
* Set by the netif driver in its init function. */
#define NETIF_FLAG_BROADCAST 0x02U
/** If set, the netif is one end of a point-to-point connection.
* Set by the netif driver in its init function. */
#define NETIF_FLAG_POINTTOPOINT 0x04U
/** If set, the interface is configured using DHCP.
* Set by the DHCP code when starting or stopping DHCP. */
#define NETIF_FLAG_DHCP 0x08U
/** If set, the interface has an active link
* (set by the network interface driver).
* Either set by the netif driver in its init function (if the link
* is up at that time) or at a later point once the link comes up
* (if link detection is supported by the hardware). */
#define NETIF_FLAG_LINK_UP 0x10U
/** If set, the netif is an ethernet device using ARP.
* Set by the netif driver in its init function.
* Used to check input packet types and use of DHCP. */
#define NETIF_FLAG_ETHARP 0x20U
/** If set, the netif is an ethernet device. It might not use
* ARP or TCP/IP if it is used for PPPoE only.*/
#define NETIF_FLAG_ETHERNET 0x40U
/** If set, the netif has IGMP capability.
* Set by the netif driver in its init function. */
#define NETIF_FLAG_IGMP 0x80U
/** Function prototype for netif init functions. Set up flags and output/linkoutput
* callback functions in this function.
* @param netif The netif to initialize
*/
typedef err_t (*netif_init_fn)(struct netif *netif);
/** Function prototype for netif->input functions. This function is saved as 'input'
* callback function in the netif struct. Call it when a packet has been received.
* @param p The received packet, copied into a pbuf
* @param inp The netif which received the packet
*/
typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
/** Function prototype for netif->output functions. Called by lwIP when a packet
* shall be sent. For ethernet netif, set this to 'etharp_output' and set
* 'linkoutput'.
* @param netif The netif which shall send a packet
* @param p The packet to send (p->payload points to IP header)
* @param ipaddr The IP address to which the packet shall be sent
*/
typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
ip_addr_t *ipaddr);
/** Function prototype for netif->linkoutput functions. Only used for ethernet
* netifs. This function is called by ARP when a packet shall be sent.
* @param netif The netif which shall send a packet
* @param p The packet to send (raw ethernet packet)
*/
typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
上面的netif结构去掉了部分条件编译选项,只列出了部分重要字段,下面简单介绍下每个字段的意义。next字段执行下一个netif结构的指针,一个设备可能有多个网络接口(比如路由器),LwIP会把所有网络接口的netif结构组成一个链表进行管理,有一个名为netif_list的全局变量指向该链表首部。
ip_addr、netmask、gw三个字段用于描述该网络接口的网络地址属性,依次称为接口IP地址、子网掩码、网关地址。IP 地址和网络接口一一对应,每个网络接口在使用前都会被分配一个IP地址;子网掩码用来判断某个目的 IP 地址与当前网络接口 IP 地址是否处于同一子网中,IP 层会选择与目的 IP 处于同一子网的网络接口来发送数据包;gw 字段在数据包的发送、转发过程中有重要作用,如果目的 IP 地址与所有网络接口都不属于同一子网,LwIP 将会把数据包发送到网关处,因为它认为网关设备会对该 IP 包进行正确的转发,此外网关也为设备提供了许多高级服务,如 DHCP、DNS 等。
input、output、linkoutput三个字段是三个函数指针,其中input用于将网络设备接收到的数据包提交给IP层,后两个函数指针用于将IP层的数据包发送到目的地址处。这三个函数指针既然是用于输入、输出数据包的,其中一个重要参数就是上一章介绍的pbuf类型,另一个参数就是现在介绍的netif类型。在网卡初始化时,需要向这三个函数指针注册相应的输入、输出函数,这也是协议栈移植的重点之一。
hwaddr_len与hwaddr[]两个字段则表示该网卡的物理地址长度和具体的地址信息,这也是网络硬件接口的唯一物理身份标识(也叫MAC地址)。mtu字段表示该网络接口可以传送的最大数据包长度,对于以太网一般设为1500,如果IP层要发送的数据包超过mtu则需要对其进行分片处理(接收方则需要在IP层将这些数据包重组还原),以便能通过该网络接口将数据包发送出去。
flags字段是网络接口的状态、属性信息字段,包括网络接口的软件使能、广播属性、ARP属性等重要控制位,在上面代码的下半部分列出了部分flags控制位及其意义。name[]字段用于保存每个网络接口的名字,可用于标识网络接口的种类。num字段用于为每个网络接口设置一个编号,用于唯一的标识各个网络接口。
loopfirst与looplast字段处于条件编译选项内,当LwIP环回接口功能LOOPBACK启用的情况下,这两个字段分别指向上层发往环回接口地址(通常为127.0.0.1)处的数据包pbuf链表的第一个pbuf和最后一个pbuf。
2.2 网络接口的操作
从网络接口结构体netif可以看出,对网络接口的操作主要有初始化、添加、移除、设置、启用、禁用等几种,其中网络接口添加函数包含了对netif结构的初始化与函数指针的注册等操作,比较具有代表性,下面展示netif_add函数的实现代码:
// rt-thread\components\net\lwip-1.4.1\src\core\netif.c
/**
* Add a network interface to the list of lwIP netifs.
*
* @param netif a pre-allocated netif structure
* @param ipaddr IP address for the new netif
* @param netmask network mask for the new netif
* @param gw default gateway IP address for the new netif
* @param state opaque data passed to the new netif
* @param init callback function that initializes the interface
* @param input callback function that is called to pass
* ingress packets up in the protocol layer stack.
*
* @return netif, or NULL if failed.
*/
struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
{
/* reset new interface configuration state */
ip_addr_set_zero(&netif->ip_addr);
ip_addr_set_zero(&netif->netmask);
ip_addr_set_zero(&netif->gw);
netif->flags = 0;
#if ENABLE_LOOPBACK
netif->loop_first = NULL;
netif->loop_last = NULL;
#endif /* ENABLE_LOOPBACK */
/* remember netif specific state information data */
netif->state = state;
netif->num = netif_num++;
netif->input = input;
netif_set_addr(netif, ipaddr, netmask, gw);
/* call user specified initialization function for netif */
if (init(netif) != ERR_OK) {
return NULL;
}
/* add this netif to the list */
netif->next = netif_list;
netif_list = netif;
return netif;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
netif_add函数完成了netif结构体的初始化,注册了数据包接收函数并回调了网络接口初始化函数,最后将该网络结构插入到netif_list链表中。网络接口的其他操作函数如下表示:
操作函数 | 功能描述 |
---|---|
void netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw) | 重新设置该网络接口的IP地址、子网掩码、网关地址 |
void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) | 重新设置该网络接口的IP地址 |
void netif_set_netmask(struct netif *netif, ip_addr_t *netmask) | 重新设置该网络接口的子网掩码 |
void netif_set_gw(struct netif *netif, ip_addr_t *gw) | 重新设置该网络接口的网关地址 |
void netif_remove(struct netif * netif) | 从netif_list中移除该网络接口,若该接口正使用 需先禁用 |
struct netif *netif_find(char *name) | 根据网络接口名查找并返回对应的接口结构netif |
void netif_set_default(struct netif *netif) | 将该网络接口netif设为系统默认网络接口 |
void netif_set_up(struct netif *netif) | 启用该网络接口 |
void netif_set_down(struct netif *netif) | 禁用该网络接口 |
void netif_set_link_up(struct netif *netif) | 网卡底层打开,表示其可进行链路数据的收发 |
void netif_set_link_down(struct netif *netif) | 网卡底层关闭,表示其不能再接收链路层数据 |
需要提醒的一点是,如果协议栈运行过程中想动态更改某网络接口的IP地址,需要在重新设置网卡地址前先禁用该网卡,当地址重新设置后再启用该网卡,否则可能会导致内存访问错误等问题。
下面以环回接口的初始化为例来看看网络接口是如何初始化的:
// rt-thread\components\net\lwip-1.4.1\src\core\netif.c
/**
* Initialize a lwip network interface structure for a loopback interface
*
* @param netif the lwip network interface structure for this loopif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
*/
static err_t netif_loopif_init(struct netif *netif)
{
/* initialize the snmp variables and counters inside the struct netif
* ifSpeed: no assumption can be made!
*/
netif->name[0] = 'l';
netif->name[1] = 'o';
netif->output = netif_loop_output;
return ERR_OK;
}
void netif_init(void)
{
#if LWIP_HAVE_LOOPIF
ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
IP4_ADDR(&loop_gw, 127,0,0,1);
IP4_ADDR(&loop_ipaddr, 127,0,0,1);
IP4_ADDR(&loop_netmask, 255,0,0,0);
#if NO_SYS
netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
#else /* NO_SYS */
netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
#endif /* NO_SYS */
netif_set_up(&loop_netif);
#endif /* LWIP_HAVE_LOOPIF */
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
三、特殊的环回接口
前面谈到环回接口可以实现对网络接口功能的完全软件模拟,使得我们可以在没有网络硬件的情况下也可以使用并调试网络协议栈;另外,环回接口为同一台设备上的两个进程间的数据通信提供了一种可行方式。环回接口采用环回IP地址,根据通常惯例,环回接口的IP地址被设为127.0.0.1。
当IP层要发送数据包的目的地址是环回接口时,环回接口注册的数据包发送函数(netif_loop_output)被调用,该函数不会像以太网接口那样使得数据包发送到物理网络中,而只是简单地将该数据包连接到自己netif结构中的loop_first链表上,该链表上的数据包只能通过调用函数netif_poll才会被递交给IP层,当有操作系统模拟层时netif_poll会被内核自动调用,无操作系统模拟层时则需用户自己在程序中调用netif_poll。两个应用程序通过环回接口进行数据交互的流程如下图示:
要使用环回接口功能,需要在配置文件中定义ENABLE_LOOPBACK(也即定义LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF),跟环回接口操作相关的函数实现代码如下:
// rt-thread\components\net\lwip-1.4.1\src\core\netif.c
#if ENABLE_LOOPBACK
/**
* Send an IP packet to be received on the same netif (loopif-like).
* The pbuf is simply copied and handed back to netif->input.
* In multithreaded mode, this is done directly since netif->input must put
* the packet on a queue.
* In callback mode, the packet is put on an internal queue and is fed to
* netif->input by netif_poll().
* @param netif the lwip network interface structure
* @param p the (IP) packet to 'send'
* @param ipaddr the ip address to send the packet to (not used)
* @return ERR_OK if the packet has been sent
* ERR_MEM if the pbuf used to copy the packet couldn't be allocated
*/
err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
{
struct pbuf *r;
err_t err;
struct pbuf *last;
SYS_ARCH_DECL_PROTECT(lev);
/* Allocate a new pbuf */
r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
if (r == NULL) {
return ERR_MEM;
}
/* Copy the whole pbuf queue p into the single pbuf r */
if ((err = pbuf_copy(r, p)) != ERR_OK) {
pbuf_free(r);
r = NULL;
return err;
}
/* Put the packet on a linked list which gets emptied through calling
netif_poll(). */
/* let last point to the last pbuf in chain r */
for (last = r; last->next != NULL; last = last->next);
SYS_ARCH_PROTECT(lev);
if(netif->loop_first != NULL) {
netif->loop_last->next = r;
netif->loop_last = last;
} else {
netif->loop_first = r;
netif->loop_last = last;
}
SYS_ARCH_UNPROTECT(lev);
#if LWIP_NETIF_LOOPBACK_MULTITHREADING
/* For multithreading environment, schedule a call to netif_poll */
tcpip_callback((tcpip_callback_fn)netif_poll, netif);
#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
return ERR_OK;
}
/**
* Call netif_poll() in the main loop of your application. This is to prevent
* reentering non-reentrant functions like tcp_input(). Packets passed to
* netif_loop_output() are put on a list that is passed to netif->input() by
* netif_poll().
*/
void netif_poll(struct netif *netif)
{
struct pbuf *in;
SYS_ARCH_DECL_PROTECT(lev);
do {
/* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
SYS_ARCH_PROTECT(lev);
in = netif->loop_first;
if (in != NULL) {
struct pbuf *in_end = in;
while (in_end->len != in_end->tot_len) {
in_end = in_end->next;
}
/* 'in_end' now points to the last pbuf from 'in' */
if (in_end == netif->loop_last) {
/* this was the last pbuf in the list */
netif->loop_first = netif->loop_last = NULL;
} else {
/* pop the pbuf off the list */
netif->loop_first = in_end->next;
}
/* De-queue the pbuf from its successors on the 'loop_' list. */
in_end->next = NULL;
}
SYS_ARCH_UNPROTECT(lev);
if (in != NULL) {
/* loopback packets are always IP packets! */
if (ip_input(in, netif) != ERR_OK) {
pbuf_free(in);
}
/* Don't reference the packet any more! */
in = NULL;
}
/* go on while there is a packet on the list */
} while (netif->loop_first != NULL);
}
#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
/**
* Calls netif_poll() for every netif on the netif_list.
*/
void netif_poll_all(void)
{
struct netif *netif = netif_list;
/* loop through netifs */
while (netif != NULL) {
netif_poll(netif);
/* proceed to next network interface */
netif = netif->next;
}
}
#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
#endif /* ENABLE_LOOPBACK */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
函数netif_poll将结构netif中loop_first链表上的所有数据包依次递交给IP层,从上面的代码可以看出,一个pbuf链表中可以保存多个数据包,只有通过判断pbuf中的tot_len字段与len字段的值相等才能确定一个数据包已经结束。
评论记录:
回复评论: