一,简介
以太网通信中,硬件层的实现是靠网卡,每个网卡都有ip地址,mac地址,最大传输包长度,输入输出功能,。lwip使用netif来描述这些网卡,并将网卡的输入输出数据传递给ip层。
二,源码分析
首先是网口的结构体,结合上图
struct netif {
struct netif *next; //指向下一个网口
ip_addr_t ip_addr; //网口ip地址
ip_addr_t netmask; //网口子网掩码,用来判断ip是否处于同一网络
ip_addr_t gw; //网关地址,若目的ip不在同一网络,则将报文发送给网关
netif_input_fn input; //网口调用该函数将数据包传递给ip层
netif_output_fn output; //ip层调用该函数将数据包传递给网口
netif_linkoutput_fn linkoutput; //网口调用该函数将数据包传递给以太网驱动
void *state;
#if LWIP_DHCP
struct dhcp *dhcp;
#endif /* LWIP_DHCP */
u16_t mtu; //最大数据包长度
u8_t hwaddr_len; //硬件地址长度
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];//硬件地址
u8_t flags; //网口的状态 属性控制位
char name[2]; //网口名称
u8_t num; //网口的编号
#if LWIP_IGMP
netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IGMP */
};
netif的初始化函数为netif_add();这个函数实际上就是用获取到的ip地址等填充netif结构体,并将网卡的初始化和输入函数赋值到netif中,调用网卡初始化函数初始化网卡,并将netif插入链表。初始化和输入函数是由网卡的驱动者提供的。
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)
{
static u8_t netifnum = 0; //静态变量:网口编号
//重置网口地址
ip_addr_set_zero(&netif->ip_addr);
ip_addr_set_zero(&netif->netmask);
ip_addr_set_zero(&netif->gw);
netif->flags = 0;
netif->dhcp = NULL;
netif->igmp_mac_filter = NULL;
netif->state = state;
netif->num = netifnum++; //新编号
netif->input = input; //添加ip层输入函数
netif_set_addr(netif, ipaddr, netmask, gw);//设置网口的地址
//调用初始化函数,初始化硬件
if (init(netif) != ERR_OK) {
return NULL;
}
//将网口插入链表头
netif->next = netif_list;
netif_list = netif;
snmp_inc_iflist();
return netif;
}
需要注意的是在添加网口的ip地址时,会检查是否有tcp连接绑定在之前的网口上,若有则需要将该tcp连接终止,因为网口的ip地址修改将导致tcp连接断开。
//设置网口ip地址
void
netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
{
#if LWIP_TCP
struct tcp_pcb *pcb;
struct tcp_pcb_listen *lpcb;
//新的ipaddr与之前的ip不一样,需要更新tcp控制块
if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
pcb = tcp_active_pcbs; //正常的tcp连接链表
while (pcb != NULL) {
//判断是否有tcp绑定到当前网口的ip上。若有则终止tcp
if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)))
{
struct tcp_pcb *next = pcb->next;//将pcb从链表删除
tcp_abort(pcb);
pcb = next;
} else {
pcb = pcb->next; //检查下一个tcp
}
}
//检查处于listen状态的tcp
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
//若tcp绑定到当前网口,则更新tcp的ip信息,此时无tcp连接
if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
(ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) {
ip_addr_set(&(lpcb->local_ip), ipaddr);
}
}
}
#endif
ip_addr_set(&(netif->ip_addr), ipaddr); //设置网口ip
}