LwIP 2.1.0学习摘要

参考:lwIP Wiki | FANDOM powered by Wikia

参考:lwIP: Overview

参考:LwIP源代码文件目录解析 - jrunw的博客 - CSDN博客

参考:LwIP协议栈开发嵌入式网络的三种方法分析 - wangyw - 博客园

参考:LWIP使用经验---变态级(好文章) - yangzhao0001的博客 - CSDN博客

参考:《LwIP协议栈源码详解——TCP/IP协议的实现》TCP坚持与保活定时器 - bian1029的专栏 - CSDN博客

参考:《嵌入式网络那些事Lwip深度剖析与实战演练》

项目Git路径:lwip.git - lwIP - A Lightweight TCPIP stack


一.简介

LwIP是原本由Swedish Institute of Computer Science (SICS)的Adam Dunkels开发,现在是由以Kieran Mansley为首的来自世界各地的开发者积极推进的一款独立的TCP/IP协议组件。

LwIP遵从BSD license,致力于全面实现TCP协议的同时减少RAM的使用量。所以LwIP特别适用于在嵌入式系统上运行,一般需要数十KByte的RAM和40KByte左右的ROM空间。LwIP已移植到多个平台或操作系统上,它既能够运行在有操作系统的环境中,亦可运行于没有操作系统的裸机环境。

LwIP支持下述各种协议:

IP (Internet Protocol) including packet forwarding over multiple network interfaces

ICMP (Internet Control Message Protocol) for network maintenance and debugging

IGMP (Internet Group Management Protocol) for multicast traffic management

UDP (User Datagram Protocol) including experimental UDP-lite extensions

TCP (Transmission Control Protocol) with congestion control, RTT estimation and fast recovery/fast retransmit

Raw/native API for enhanced performance

Optional Berkeley-like socket API

DNS (Domain names resolver)

SNMP (Simple Network Management Protocol)

DHCP (Dynamic Host Configuration Protocol)

AUTOIP (for IPv4, conform withRFC 3927)

PPP (Point-to-Point Protocol)

ARP (Address Resolution Protocol) for Ethernet

LwIP为用户提供三组API接口,

sequential API(Netconn API) 为普通的、顺序的程序提供了使用lwIP栈的方法。依赖操作系统,所有操作都需要协议栈去处理,应用程序与协议栈通信,通过发送消息方式进行,因此这种方式会造成频繁的任务切换,速度相比RAW API慢了许多。在操作系统上推荐使用本API

BSD Socket API  建立在sequential API之上的,封装出的一套BSD Socket API。同样依赖操作系统,效率低,消耗的资源更多。

Raw API(native API / callback) 基于零拷贝发送和接收的事件驱动的API,在没有操作系统的情况下唯一可用的API。操作较复杂但速度最快,效率最高。

※三种API的详细区别可参考:lwIP: APIs


二.使用方法

1.无操作系统模式

1)无OS宏定义:#define NO_SYS to 1

2)在主循环中将接收到的packet传入netif->input(pbuf, netif),不要在中断中进行调用。可以考虑在中断中开辟PBUF空间,然后将其放在队列中待主循环处理

3)主循环中定期调用sys_check_timeouts()  

4)实现这几个模块的所有函数:Time, Critical sections and Compiler/platform abstraction

5)此模式下只能使用Raw API

6)此模式下使用lwip_init()

※详见:lwIP: Mainloop mode ("NO_SYS")


2.操作系统模式

1)选择一款能够正确处理优先级反转(priority inversion)的OS,并#define LWIP_TCPIP_CORE_LOCKING   1

2)实现Porting (system abstraction layer)的所有函数

3)可以搭配使用Raw APItcpip_callback,或者直接使用sequential API

4)此模式下使用tcpip_init()

※详见:lwIP: OS mode (TCPIP thread)


三.目录结构

LwIP

|-- doc 文档

|-- test 测试case

|-- src 源码

      |-- api                              high-level API接口(sequential API、BSD Socket API )如果只是使用Raw API,则不需要该目录。

               |-- api_lib.c            Sequential API External module

               |-- api_msg.c         Sequential API Internal module

               |-- err.c                  Error Management module

               |-- if_api.c              Interface Identification APIs from:

                                            RFC 3493: Basic Socket Interface Extensions for IPv6

                                            Section 4: Interface Identification

               |-- netbuf.c            Network buffer management

               |-- netdb.c             API functions for name resolving

               |-- netifapi.c          Network Interface Sequential API module

               |-- sockets.c         Sockets BSD-Like API module

               |-- tcpip.c             Sequential API Main thread module

      |-- apps                         特别采用low-level Raw API编写的应用例程。

      |-- core                          TPC/IP协议栈内核、协议实现、内存/缓存管理、low-level Raw API。

               |-- ipv4                  包含了IPv4标准中与IP层数据包处理相关的所有代码

               |-- ipv6                  包含了IPv6标准中与IP层数据包处理相关的所有代码

               |-- altcp_alloc.c     Application layered TCP connection API (to be used from TCPIP thread)

                                            This interface mimics the tcp callback API to the application while preventing

                                            direct linking (much like virtual functions).

                                            This way, an application can make use of other application layer protocols

                                            on top of TCP without knowing the details (e.g. TLS, proxy connection).

                                            This file contains allocation implementation that combine several layers.

               |-- altcp_tcp.c        Application layered TCP connection API (to be used from TCPIP thread)\n

                                            This interface mimics the tcp callback API to the application while preventing

                                            direct linking (much like virtual functions).

                                            This way, an application can make use of other application layer protocols

                                            on top of TCP without knowing the details (e.g. TLS, proxy connection).

                                            This file contains the base implementation calling into tcp.

               |-- altcp.c              common functions for altcp(application layered TCP connection API; to be used from TCPIP thread) to work

               |-- def.c                 Common functions used throughout the stack(如lwip_htons、lwip_htonl)

               |-- dns.c                DNS - host name to IP address resolver

               |-- inet_chksum.c   Internet checksum functions

               |-- init.c Modules    initialization

               |-- ip.c                  Common IPv4 and IPv6 code

               |-- mem.c            Dynamic memory manager

               |-- memp.c          Dynamic pool memory manager

               |-- netif.c              lwIP network interface abstraction

               |-- pbuf.c              Packet buffer management

               |-- raw.c              Implementation of raw protocol PCBs for low-level handling of

                                         different types of protocols besides (or overriding) those

                                          already available in lwIP.

               |-- stats.c           Statistics module. 主要用来统计调试。

               |-- sys.c              lwIP Operating System abstraction

               |-- tcp.c              Transmission Control Protocol for IP

               |-- tcp_in.c         Transmission Control Protocol, incoming traffic

                                        The input processing functions of the TCP layer.

                                        These functions are generally called in the order (ip_input() ->)

                                        tcp_input() -> * tcp_process() -> tcp_receive() (-> application).

               |-- tcp_out.c     Transmission Control Protocol, outgoing traffic

                                        The output functions of TCP.

               |-- timeout.c     Stack-internal timers implementation.

                                        This file includes timer callbacks for stack-internal timers as well as

                                        functions to set up or stop timers and check for expired timers.

               |-- udp.c           User Datagram Protocol module

                                        The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).

              |-- include          lwIP头文件.

                       |-- compat                  兼容

                                  |-- posix         POSIX标准下的封装

                                  |-- stdc           posix/stdc的封装

                       |-- netif      针对netif目录中源文件的各种头文件

                       |-- lwip      LwIP的各种头文件,其中需要注意的有opt.h 文件,它包含了所有LwIP内核参数的默认配置值;

                                        还有init.h 文件,它包含了与当前 LwIP 源代码信息相关的宏定义,如协议版本号、是否为官方版等

              |-- netif               通用的网络接口设备驱动(不包含任何硬件和平台相关代码)

                       |-- ppp      Point-to-Point Protocol stack

                                        The lwIP PPP support is based from pppd (http://ppp.samba.org) with

                                        huge changes to match code size and memory requirements for embedded devices.

                       |-- bridgeif.c                          implementing an IEEE 802.1D MAC Bridge

                       |-- bridgeif_fdb.c                   implementing an FDB for IEEE 802.1D MAC Bridge

                       |-- ethernet.c                         Shared code for Ethernet based interfaces.

                       |-- lowpan6.c                         A 6LoWPAN implementation as a netif.

                       |-- lowpan6_common.c         A 6LoWPAN over Bluetooth Low Energy (BLE) implementation as netif,  according to RFC-7668.

                       |-- slipif.c                               A generic implementation of the SLIP (Serial Line IP)

                                                                    protocol. It requires a sio (serial I/O) module to work.

                       |-- zepif.c                              implementing the ZigBee Encapsulation Protocol (ZEP).


四.LwIP启动过程



LwIP启动过程


五.源码分析

1.内存管理

LwIP支持内存池memp和内存堆mem两种内存管理方式。

内存池的特点是预先开辟多组固定大小的内存块组织成链表,实现简单,分配和回收速度快,不会产生内存碎片,但是大小固定,并且需要预估算准确。

内存堆的本质是对一个事先定义好的内存块进行合理有效的组织和管理,主要用于任意大小的内存分配,实现较复杂,分配需要查找,回收需要合并,容易产生内存碎片,需要合理估算内存堆的总大小。


Packet buffers(pbufs)

pbuf是LwIP中数据包的内部存储方式,类似于BSD的mbuf。同时支持使用动态内存保存包内容,以及使用静态内存保存内容。

/** Main packet buffer */

struct pbuf {

    struct pbuf *next;       /** 指向下一个pbuf */

    void *payload;            /** 指向载荷的起始地址 */

    u16_t tot_len;             /** 本buffer的长度加上属于同一个packet的buffer长度的总和。

                                            For non-queue packet chains this is the invariant: p->tot_len == p->len + (p->next? p->next->tot_len: 0) */

    u16_t len;                   /** 本buffer的长度 */

    u8_t type_internal;     /** 用来表示pbuf类型以及资源分配的位域。

                                          (see PBUF_TYPE_FLAG_*, PBUF_ALLOC_FLAG_* and PBUF_TYPE_ALLOC_SRC_MASK)    */

    u8_t flags;                  /** misc flags */

    LWIP_PBUF_REF_T ref;    /**  本pbuf的参考引用计数。初始为1,大于1时无法删除。

                                                      This can be pointers from an application, the stack itself, or pbuf->next pointers from a chain.   */

    u8_t if_idx;                 /** 对于接收到的packet,用来保存输入netif的索引号 */

    LWIP_PBUF_CUSTOM_DATA    /** 用来保存用户自定义数据,如timestamps,需要宏定义 */

};


共有四种pbuf类型,PBUF_RAM、PBUF_ROM、PBUF_POOL、PBUF_REF。

pbuf类型与特点
PBUF四种类型


PBUF_RAM

保存在RAM中的pbuf,主要用来进行TX发送。struct pbuf和payload会被分配在一个连续的内存空间。


PBUF_ROM

data数据保存在ROM中的pbuf,struct pbuf和payload被分配在不同的存储空间。因为payload指向ROM,数据传递时payload无需被拷贝。


PBUF_POOL

payload指向RAM,来自一个内存池并且主要用来进行RX接收。可以向PBUF_RAM一样链成链表,

struct pbuf和payload被分配在一个连续的存储空间。不可用来进行TX。适合用于中断处理函数。


PBUF_REF

pbuf来自PBUF_POOL。有点像PBUF_ROM,但由于payload有可能发生变化,所以需要根据谁在引用它在队列传输时进行拷贝。


内存管理

使用一个专用区域,确保网络系统不会用光所有内存。它掌管连续内存的开辟和释放,并且能够压缩之前分配出去的内存大小。

内存分配时,会把检索到第一块足够大的空间分配出去。通过记录每块空间的used flag,内存释放时可将前后同为未使用的内存进行合并。


内存管理结构


2.网络接口

netif用来抽象网卡设备,支持多网卡设备。如ethernet、loopback等。

/** Generic data structure used for all lwIP network interfaces */

struct netif {

  struct netif *next; /** 指向下一个netif */

  ip_addr_t ip_addr; /** IPv4地址 */

  ip_addr_t netmask; /** IPv4掩码地址 */

  ip_addr_t gw; /** IPv4网关地址 */

  ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; /** IPv6地址数组 */

  u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; /** IPv6地址状态数组 */

  u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];

  u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];

  netif_input_fn input; /** 被网络设备驱动调用的、用来向TCP/IP协议栈传递packet */

  netif_output_fn output; /** 被IP模块调用的、用来向网卡发送packet。对于ethernet物理层,通常是etharp_output() */

  netif_linkoutput_fn linkoutput; /** 想要在该网卡上发送packet时,该函数会被etharp_output()调用。会像在链接上传输pbuf一样 */

  netif_output_ip6_fn output_ip6; /** 想要在该网卡上发送packet时,该函数会被IPv6模块调用。对于ethernet物理层,通常是ethip6_output() */

  netif_status_callback_fn status_callback; /** 当netif状态被设置为up或down时函数会被调用 */

  netif_status_callback_fn link_callback; /** 当netif链接被设置为up或down时函数会被调用 */

  netif_status_callback_fn remove_callback; /** 当netif被移除时函数会被调用 */

  void *state; /** 被设备驱动设定并指向设备的状态信息 */

  void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];

  const char*  hostname; /** 该netif的主机名 */

  u16_t chksum_flags; /** checksum标志 */

  u16_t mtu; /** maximum transfer unit (in bytes) */

  u16_t mtu6; /** maximum transfer unit (in bytes) for IPv6 */

  u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; /** link level hardware address of this interface */

  u8_t hwaddr_len; /** number of bytes used in hwaddr */

  u8_t flags; /** netif标志(netif是否设置为有效等) */

  char name[2]; /** 描述缩略语,如WLAN->wl */

  u8_t num; /** number of this interface. as well as for IPv6 zones */

  u8_t ip6_autoconfig_enabled; /** 本netif对于IPv6自动配置是否有效 */

  u8_t rs_count; /** Number of Router Solicitation messages that remain to be sent. */

  //用于stats start

  u8_t link_type; /** link type (from "snmp_ifType" enum from snmp_mib2.h) */

  u32_t link_speed; /** (estimate) link speed */

  u32_t ts; /** timestamp at last change made (up/down) */

  struct stats_mib2_netif_ctrs mib2_counters; /** counters */

  //用于stats end

  netif_igmp_mac_filter_fn igmp_mac_filter; /** could be called to add or delete an entry in the multicast filter table of the ethernet MAC.*/

  netif_mld_mac_filter_fn mld_mac_filter; /** could be called to add or delete an entry in the IPv6 multicast filter table of the ethernet MAC. */

  struct acd *acd_list; /** ACD state information */

  struct pbuf *loop_first; /* List of packets to be queued for ourselves. loop interface (127.0.0.1)*/

  struct pbuf *loop_last;

  u16_t loop_cnt_current;

};

全局变量 struct netif *netif_list 和 struct netif *netif_default 分别用来指向netif链表表头和默认netif。

netif追加


netif_add()



3.IP处理

ip.c定义了一些共通的IP代码。IPv4、IPv6的具体实现依赖相关文件夹下的代码。

基本数据结构ip_pcb

/* Common members of all PCB types */

struct ip_pcb {

  ip_addr_t local_ip;                     

  ip_addr_t remote_ip;                   

  u8_t netif_idx;           /* Bound netif index */

  u8_t so_options;       /* Socket options(bits域:本地地址可重用、keepalive、广播) */

  u8_t tos;                     /* Type Of Service */

  u8_t ttl;                     /* Time To Live */

};


ip_input与ip_output

以IPv4为例,

err_t ip4_input(struct pbuf *p, struct netif *inp);    /* 当网卡驱动收到IP包时会调用该函数,该函数进行一些基本检查,如果该包的目的地不是这里将转发给其他网卡,最后该包会被传递给上层input函数 */

err_t ip_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto);    /* 将IP包发送至网卡。该函数组成IP报头并计算checksum */

4.UDP处理


基本数据结构udp_pcb

/** the UDP protocol control block */

struct udp_pcb {

  IP_PCB;                /** Common members of all PCB types */

  struct udp_pcb *next;        /* Protocol specific PCB members */

  u8_t flags;

  u16_t local_port, remote_port;     /** ports are in host byte order */

  ip4_addr_t mcast_ip4;        /** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */

  u8_t mcast_ifindex;        /** outgoing network interface for multicast packets, by interface index (if nonzero) */

  u8_t mcast_ttl;            /** TTL for outgoing multicast packets */

  u16_t chksum_len_rx, chksum_len_tx;        /** used for UDP_LITE only */

  udp_recv_fn recv;            /** receive callback function */

  void *recv_arg;            /** user-supplied argument for the recv callback */

};


UDP操作API

struct udp_pcb * udp_new (void);        /* 创建一个udp_pcb,绑定一个端口或者连接到远端之前不被激活(指加入udp_pcbs) */

struct udp_pcb * udp_new_ip_type(u8_t type);    /* 创建一个IPv4/6的udp_pcb */

void            udp_remove    (struct udp_pcb *pcb);    /* 删除并释放pcb */

err_t            udp_bind      (struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port);       /* 绑定pcb */

void            udp_bind_netif (struct udp_pcb *pcb, const struct netif* netif);    /* 绑定pcb到一个netif,之后所有该pcb输出输入都会通过该netif */

err_t            udp_connect    (struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port);    /* 设置远端IP和端口,无连接保障 */

void            udp_disconnect (struct udp_pcb *pcb);    /* 清楚远端IP和端口,无连接保障 */

void            udp_recv      (struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg);   /* 设置接受用回调函数,该函数在收到该pcb的报文时会被调用 */

err_t            udp_sendto_if  (struct udp_pcb *pcb, struct pbuf *p,

                                const ip_addr_t *dst_ip, u16_t dst_port,

                                struct netif *netif);    /* 使用指定netif发送pbuf到指定地址 */

err_t            udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,

                                const ip_addr_t *dst_ip, u16_t dst_port,

                                struct netif *netif, const ip_addr_t *src_ip);    /* 类udp_sendto_if() */

err_t            udp_sendto    (struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port);    /* 使用udp_pcb发送pbuf到指定地址 */

err_t            udp_send      (struct udp_pcb *pcb, struct pbuf *p);     /* 使用udp_pcb发送pbuf到当前远端地址(pcb->remote_ip) */

void            udp_input (struct pbuf *p, struct netif *inp);     /* 处理一个接收到的UDP数据包。该函数找到相应udp_pcb处理该包,并调用recv函数 */

void            udp_init      (void);     /* 初始化(初始UDP端口) */

5.TCP处理

详见:LwIP 2.1.0 TCP学习摘要 -


六.移植

未完待续。

你可能感兴趣的:(LwIP 2.1.0学习摘要)