LWIP --------- netif网卡接口

目录

1、概念&作用

2、netif结构

2.1、网卡链表

2.2、网络IP

2.3、接收数据函数

2.4、发送数据函数

[2.5、ARP 模块调用的发送函数](#2.5、ARP 模块调用的发送函数)

2.6、出口回调函数

2.7、用户私有数据

2.8、最大传输单位

2.9、链路硬件地址长度&地址

2.10、网卡信息状态标志

2.11、网卡名字

2.12、网卡标识


1、概念&作用

网络接口(以太网接口)是硬件接口(网络接口又可以称之为网卡)。LWIP为了兼容不同的网卡,使用了一个 netif结构体来描述网卡。

其中,etherneif.c 文件则提供了 netif 访问的各种不同网卡,每个网卡有不同的实现方式,用户只需修改 etherneif.c 文件即可。

2、netif结构

2.1、网卡链表

cpp 复制代码
struct netif *next;

LWIP使用链表来统一管理统一设备的多个网卡。

neif.c 文件中定义了两个全局指针 struct netif *netif_liststruct netif *netif_default

  • netif_list 就是网卡链表指针,指向网卡链表的首节点(第一个网卡)。
  • netif_default 默认网卡。

2.2、网络IP

cpp 复制代码
#if LWIP_IPV4
  /** IP address configuration in network byte order */
  ip_addr_t ip_addr; // 网络中的 IP 地址
  ip_addr_t netmask; // 子网掩码
  ip_addr_t gw;      // 网关地址
#endif /* LWIP_IPV4 */

2.3、接收数据函数

cpp 复制代码
typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);

/** 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 为接收到的数据包。

2.4、发送数据函数

cpp 复制代码
struct ip4_addr {
  u32_t addr;
};

typedef struct ip4_addr ip4_addr_t;

typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);

#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 类型。

注意:代表要将数据包发送到的地址,但不一定数据包最终到达的 ip 地址,该数据包要被发送到一个路由器上,这里的 ipaddr 就是路由器的 ip 地址;

2.5、ARP 模块调用的发送函数

cpp 复制代码
typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);

/** 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() 函数调用。

2.6、出口回调函数

cpp 复制代码
typedef void (*netif_status_callback_fn)(struct netif *netif);

#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 被删除时调用此函数。

2.7、用户私有数据

cpp 复制代码
/** 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*/

由设备驱动程序设置并指向设备的状态信息,主要将网卡的某些私有数据传递给上层。

2.8、最大传输单位

cpp 复制代码
/** maximum transfer unit (in bytes) */
  u16_t mtu;

2.9、链路硬件地址长度&地址0

cpp 复制代码
/** number of bytes used in hwaddr */
  u8_t hwaddr_len;
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];

2.10、网卡信息状态标志

cpp 复制代码
/** flags (@see @ref netif_flags) */
  u8_t flags;

网卡状态信息标志位,是很重要的控制字段,它包括网卡的功能使能,广播使能,ARP 使能等重要控制位。

2.11、网卡名字

cpp 复制代码
/** descriptive abbreviation */
  char name[2];

用于保存每一个网卡的名字,用两个字符的名字来标识。

网卡使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡表示的硬件的种类。

如果两个网卡具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡。

2.12、网卡标识

cpp 复制代码
/** number of this interface */
  u8_t num;

标识使用同种驱动类型的不同网卡。

netif 使用

简要步骤:

  1. 定义一个 netif 作为网卡设备结构体。

  2. 挂载到 netif_list 链表中。

    1. 使用函数:netif_add();
    2. 建议参考该函数源码;

具体使用方法:

cpp 复制代码
// 1. 定义 netif 结构体
static struct netif my_netif;

// 2. 定义 IP 地址(例如使用静态IP)
ip4_addr_t ipaddr, netmask, gw;
IP4_ADDR(&ipaddr, 192, 168, 1, 100);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);

// 3. 实现底层初始化回调函数 (netif_init_fn)
static err_t myif_init(struct netif *netif) {
    // 设置网卡名称,例如 "en0"
    netif->name[0] = 'e';
    netif->name[1] = 'n';

    // 设置MAC地址 (假设存储在 netif->state 中)
    // my_private_data *priv = (my_private_data*)netif->state;
    // memcpy(netif->hwaddr, priv->mac_addr, 6);
    netif->hwaddr_len = 6;

    // 设置底层数据发送函数 (linkoutput)
    netif->linkoutput = myif_linkoutput; // myif_linkoutput 需要另外实现

    // 初始化硬件寄存器、中断等...
    // if (my_hardware_init(priv) != ERR_OK) return ERR_IF;

    return ERR_OK;
}

// 4. 在网卡驱动初始化函数中
void my_network_driver_init(void) {
    struct netif *p_netif = NULL;

    // 可以定义一些私有数据,通过 state 参数传递
    // my_private_data priv = { ... };

    // 5. 调用 netif_add 添加接口
    p_netif = netif_add(&my_netif, 
                        &ipaddr, &netmask, &gw, // IP配置
                        NULL,                    // state, 这里没用到私有数据
                        myif_init,               // 初始化回调
                        tcpip_input);            // input 回调 (多线程环境)
    
    if (p_netif != NULL) {
        // 6. 添加成功后,通常还需要:
        //    将此接口设置为默认接口
        netif_set_default(&my_netif);
        //    将接口状态设置为 "up",使其可以处理流量
        netif_set_up(&my_netif);
    } else {
        // 处理错误
    }
}

3、struct netif

cpp 复制代码
/** 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 */
};
相关推荐
Du_chong_huan2 小时前
5.1 Web 服务器的部署位置
网络
IpdataCloud3 小时前
资源受限设备上轻量级IP查询模块的部署方法
网络·数据库·网络协议·tcp/ip
韭菜张师傅3 小时前
Ceph MDS 命令详解
网络·ceph
浪游东戴河4 小时前
网线简介及分类
运维·服务器·网络
羸弱的穷酸书生4 小时前
跟AI学一手之渗透测试智能体
网络·人工智能
榴莲omega4 小时前
第7天:网络进阶——TCP/UDP、WebSocket、长连接
javascript·网络·tcp/ip·udp·八股
@猿程序5 小时前
ShardingSphere自定义分片算法与Redis动态规则加载实战
网络·redis·算法
Du_chong_huan5 小时前
《网络是怎样连接的》精读版 第五章总述
网络·计算机网络
feng一样的男子5 小时前
Rocky Linux 9 配置 IPv6 完整指南
linux·网络