TCP/IP协议与lwip库——源代码分析(二)

目录

  • `netif_init();`
    • 环回地址
    • 添加网络接口
  • `socket`
  • `udp_init()/tcp_init()`
  • DHCP
    • LWIP实现DHCP
    • DHCP报文格式
  • `SNMP`

前述链接在此~
TCP/IP协议与lwip库——源代码分析(一)
这一章会把lwip的初始化函数分析完毕,加油Ψ( ̄∀ ̄)Ψ
话说我立志每次换一个颜文字来写哈哈哈,看我能不能用完搜狗里面所有的颜文字~~

netif_init();

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 */
}

环回地址

LWIP_HAVE_LOOPIF

LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1). This is only needed when no real netifs are available. If at least one other netif is available, loopback traffic uses this netif.

《TCP/IP详解》中这样说:
大多数产品都支持环回接口(Loopback Interface),以允许运行在同一台主机上的客户程序和服务器程序通过TCP/IP进行通信,A类网络号127就是为环回接口预留的,大多数系统会把127.0.0.1分配给这个接口,并命名为localhost
TCP/IP协议与lwip库——源代码分析(二)_第1张图片
在本应用中:

#define LWIP_HAVE_LOOPIF                0

所以实际上不会执行netif_init中的任何语句,但我们还是来看看这个函数内部干了啥:

typedef struct ip_addr ip_addr_t;

ip_addr_tip_addr结构体的别称

/* This is the aligned version of ip_addr_t,
   used as local variable, on the stack, etc. */
struct ip_addr {
  u32_t addr;    // 是ip_addr_t的对齐版本?使用局部变量,和栈
};

也就是说loop_ipaddr, loop_netmask, loop_gw;分别包含一个32bit的地址

#define IP4_ADDR(ipaddr, a,b,c,d) \
        (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
                         ((u32_t)((c) & 0xff) << 16) | \
                         ((u32_t)((b) & 0xff) << 8)  | \
                          (u32_t)((a) & 0xff)

拆成四个部分(以.为分割点)进行赋值,每个部分8bit

添加网络接口

Add a network interface to the list of lwIP netifs.

// 代码有删减
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)
{
// 参数说明:netif:预分配的netif结构; IP地址/掩码/网关;state:传递给新netif的不透明数据
// init:初始化接口的回调函数;input:pass的回调函数?
  LWIP_ASSERT("No init function given", init != NULL);

  /* 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 LWIP_DHCP
  /* netif not under DHCP control by default */
  netif->dhcp = NULL;
#endif /* LWIP_DHCP */

  /* remember netif specific state information data */
  // 用于记录相关的信息
  netif->state = state;
  netif->num = netif_num++;
  netif->input = input;
  NETIF_SET_HWADDRHINT(netif, NULL);

  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;
  snmp_inc_iflist();

  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
    netif->name[0], netif->name[1]));
  ip_addr_debug_print(NETIF_DEBUG, ipaddr);
  LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
  ip_addr_debug_print(NETIF_DEBUG, netmask);
  LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
  ip_addr_debug_print(NETIF_DEBUG, gw);
  LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
  return netif;
}

结构体定义:

/** Function prototype for netif init functions. Set up flags and output/linkoutput
 * callback functions in this function.
 * netif初始化函数的函数原型。 在此函数中设置标志和output / linkoutput回调函数。
 * @param netif The netif to initialize
 */
typedef err_t (*netif_init_fn)(struct netif *netif);

err_t = s8_t = signed char
struct netif *netif是一个结构体指针,*netif_init_fn也是一个指针,这个指针指向一个函数,函数的输入参数是上述的结构体指针,返回值是有符号字符类型;这里涉及到typedef的非通用用法:为复杂的声明定义一个简单的别名
复杂声明的看法:从左到右,看到)就返回,往左看,再往右看()就表示是函数,再往左看…)
那么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 */
// 所有lwip网络接口的通用数据结构,设备驱动程序的初始化功能应填写以下字段:hwaddr_len,hwaddr [],mtu,flags
struct netif {
  struct netif *next;   /** pointer to next in linked list 可以看出来是一个链接组织结构*/

  /** 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;   // 网络设备驱动程序调用此函数以将数据包向上传递到TCP/IP堆栈,也就是将网卡接收数据交给IP层,注意这是一个回调函数
  /** 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;  // IP模块要在接口上发送数据包时,会调用此功能,通常首先解析硬件地址,然后发送数据包
  // 这个一般会使用etharp.c中的etharp_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; // ARP模块发送数据包函数
  /** This field can be set by the device driver and could point
   *  to state information for the device. */
  void *state;   // 该字段可以由设备驱动程序设置,并且可以指向设备的状态信息。
#if LWIP_DHCP
  /** the DHCP client state information for this netif */
  struct dhcp *dhcp;
#endif /* LWIP_DHCP */
  /** maximum transfer unit (in bytes) */
  u16_t mtu;   // 最大传输单元(按字节)以太网为1500
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;     // 硬件地址长度
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];   // 接口的连接层硬件地址(MAC地址)   NETIF_MAX_HWADDR_LEN = 6
  /** flags (see NETIF_FLAG_ above) */
  u8_t flags;      // 标志位,表示网卡需要什么样的功能
  /** descriptive abbreviation */
  char name[2];   // 描述缩写,网卡的名字
  /** number of this interface */
  u8_t num;   
   // 接口号,如果两个网络接口具有相同的描述缩写(即上面的name字段),就用num字段来区分相同类型的不同网络接口
};

这里是省略了使用SNMP变量的部分
这是lwip的多网口设计,这里用netif来描述每种网络接口(网卡)的特性,并且使用链表来管理,当上层有数据要发送的时候,lwip会从netif_list中选择一个合适的网卡来将数据发送出去
下面就是设置地址的具体操作函数:

void netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
    ip_addr_t *gw){
  netif_set_ipaddr(netif, ipaddr);
  netif_set_netmask(netif, netmask);
  netif_set_gw(netif, gw);
}

netif_set_ipaddr函数定义如下,用于重新设置网络接口的IP地址

// 代码有删减
void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) {
  /* TODO: Handling of obsolete pcbs */
  /* See:  http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
#if LWIP_TCP
  struct tcp_pcb *pcb;
  struct tcp_pcb_listen *lpcb;

  /* address is actually being changed? */
  if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
    /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
    pcb = tcp_active_pcbs;
    while (pcb != NULL) {
      /* PCB bound to current local interface address? */
      if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))
        ) {
        /* this connection must be aborted */
        struct tcp_pcb *next = pcb->next;
        LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
        tcp_abort(pcb);
        pcb = next;
      } else {
        pcb = pcb->next;
      }
    }
    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
      /* PCB bound to current local interface address? */
      if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
          (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) {
        /* The PCB is listening to the old ipaddr and
         * is set to listen to the new one instead */
        ip_addr_set(&(lpcb->local_ip), ipaddr);
      }
    }
  }
#endif
  snmp_delete_ipaddridx_tree(netif);
  snmp_delete_iprteidx_tree(0,netif);
  /* set new IP address to netif */
  ip_addr_set(&(netif->ip_addr), ipaddr);
  snmp_insert_ipaddridx_tree(netif);
  snmp_insert_iprteidx_tree(0,netif);

  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
    netif->name[0], netif->name[1],
    ip4_addr1_16(&netif->ip_addr),
    ip4_addr2_16(&netif->ip_addr),
    ip4_addr3_16(&netif->ip_addr),
    ip4_addr4_16(&netif->ip_addr)));
}

我们看一下这段代码:

#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);

NO_SYS = 0/1的区别在于,最后一个回调函数不同,我们对照着来看:

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)
  ......
  netif->input = input;

也就是说用于将网卡数据交给ip层的回调函数不同,那么怎么个不同法:
第一个:(代码太长,此处就不贴上来了)

err_t ip_input(struct pbuf *p, struct netif *inp)

输入参数:pbuf(用于管理数据包的结构体),netif。输出参数是一个字符型变量

struct pbuf {
  struct pbuf *next;    // 链表组织结构,指向下一个数据包
  void *payload;   // 指向缓冲区中实际数据的指针
  u16_t tot_len;   // 此缓冲区以及链中属于同一数据包的所有下一个缓冲区的总长度
  				// p->tot_len == p->len + (p->next? p->next->tot_len: 0)
  u16_t len;   // 该缓冲区的总长度
  u8_t /*pbuf_type*/ type;  // pbuf_type as u8_t instead of enum to save space
  u8_t flags;  // misc flags
  u16_t ref;  // 引用计数始终等于引用此pbuf的指针的数量。
};

收到IP数据包时,网络接口设备驱动程序将调用此功能。 该函数对IP报头进行基本检查,例如数据包大小至少大于报头大小等。如果该数据包不是发给我们的,则转发该数据包(使用ip_forward)。 始终检查IP校验和。比如:

/* identify the IP header */
iphdr = (struct ip_hdr *)p->payload;
if (IPH_V(iphdr) != 4) {
  LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
  ip_debug_print(p);
  pbuf_free(p);
  IP_STATS_INC(ip.err);
  IP_STATS_INC(ip.drop);
  snmp_inc_ipinhdrerrors();
  return ERR_OK;
}

此处有对pbuf数据包的详细解释,马克以下~
而这个函数tcpip_input不知道为啥没有办法找到定义

接下来就是打开一个接口,使能网卡,设置NETIF_FLAG_UP标志位,必须在网卡被使用前用户来调用

void netif_set_up(struct netif *netif)

主要的语句是下面这一句,用于打开网卡

if (!(netif->flags & NETIF_FLAG_UP)) {
    netif->flags |= NETIF_FLAG_UP;
    ......

我们来看一下都有些什么样的flags

#define NETIF_FLAG_UP           0x01U
/*网络接口是否为“打开”状态。 这是一个软件标志,用于控制是否启用此网络接口并处理流量。 它由启动代码(用于静态IP配置)或分配地址后的dhcp / autoip设置。*/
#define NETIF_FLAG_BROADCAST    0x02U
// 设置则表明netif具有广播功能。由netif驱动程序在其init函数中进行设置
#define NETIF_FLAG_POINTTOPOINT 0x04U
// 如果设置,netif是点对点连接的一端。 由netif驱动程序在其init函数中设置
#define NETIF_FLAG_DHCP         0x08U
// 如果设置了接口,则使用DHCP配置接口。在启动或停止DHCP时由DHCP代码设置
#define NETIF_FLAG_LINK_UP      0x10U
/*如果设置了该接口,则该接口具有活动的链接(由网络接口驱动程序设置),或者由netif驱动程序在其init函数中设置(如果该链接当时处于打开状态),或者在该链接出现后的稍后时间设置(如硬件支持链接检测)*/
#define NETIF_FLAG_ETHARP       0x20U
// 如果设置,则netif是使用ARP的以太网设备。 由netif驱动程序在其init函数中设置。 用于检查输入数据包类型和DHCP的使用
#define NETIF_FLAG_ETHERNET     0x40U
// 如果设置,则netif是以太网设备。 如果仅用于PPPoE,则可能不使用ARP或TCP / IP
#define NETIF_FLAG_IGMP         0x80U
// 如果设置,则netif具有IGMP功能

接下来执行的是这一句:

etharp_gratuitous(netif);

解释一下什么叫做**Gratuitous ARP
当设备接入网络的时候,需要先发一个信息询问一下其它设备:我发的这个IP谁有?如果网络中存在该IP,则会回复;正式利用Gratuitous ARP这个东西
对回复进行判断**,如果接收到的响应或者回复,存在IP与设备自身相同,则标记为IP冲突;MAC冲突同理。

socket

在本应用中,LWIP_SOCKET的宏定义值为0
那么什么事socket通信呢?其实在python爬虫中,也会用到这个socket,其实socket就是TCP的介质,是TCP/IP协议的API(TCP是数据的介质)
LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
这篇文章有很详细的介绍

udp_init()/tcp_init()

void udp_init(void)
{
#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
  udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
}

LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS:随机化第一个本地TCP / UDP pcb的本地端口(默认== 0),这样可以防止在引导设备后创建可预测的端口号

void tcp_init(void)
{
#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
  tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
}

也就是将第一个本地端口随机初始化的意思?

DHCP

Dynamic Host Configuration Protocol:动态主机配置协议
特点是:局域网基于UDP协议
用途是:内部网或网络服务供应商自动分配IP地址,给用户用于内部网管理员作为对所有计算机进行中央管理
TCP/IP协议与lwip库——源代码分析(二)_第2张图片
来看启动网络接口的DHCP协商的代码:

err_t dhcp_start(struct netif *netif)
{
  struct dhcp *dhcp;
  err_t result = ERR_OK;

  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
  dhcp = netif->dhcp;
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
  /* Remove the flag that says this netif is handled by DHCP,
     it is set when we succeeded starting. */
  netif->flags &= ~NETIF_FLAG_DHCP;  // 删除表示该netif由DHCP处理的标志,它是在成功启动后设置的。

  /* check hwtype of the netif */
  if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
    return ERR_ARG;
  }

  /* check MTU of the netif   MTU:最大传输单元*/ 
  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
    return ERR_MEM;
  }

  /* no DHCP client attached yet? */
  if (dhcp == NULL) {   // 检测没有DHCP客户端连接
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));  // 内存分配
    if (dhcp == NULL) {
      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
      return ERR_MEM;   // 表示内存溢出
    }
    /* store this dhcp client in the netif */
    netif->dhcp = dhcp;
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
  /* already has DHCP client attached */
  } else {
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
    if (dhcp->pcb != NULL) {
      udp_remove(dhcp->pcb);
    }
    LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
  }

  /* clear data structure */
  memset(dhcp, 0, sizeof(struct dhcp));   // 清除数据结构中原有的数据,全部置0
  /* dhcp_set_state(&dhcp, DHCP_OFF); */
  /* allocate UDP PCB */
  dhcp->pcb = udp_new();  // 注意这里使用的是UDP协议
  if (dhcp->pcb == NULL) {
    LWIP_DEBUGF(DHCP_DEBUG  | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
    return ERR_MEM;
  }
  ip_set_option(dhcp->pcb, SOF_BROADCAST);
  /* set up local and remote port for the pcb 设置pcb的本地和远程端口*/
  udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); // DHCP_CLIENT_PORT = 68
  udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);  // DHCP_SERVER_PORT = 67
  /* set up the recv callback and argument */
  udp_recv(dhcp->pcb, dhcp_recv, netif);
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
  /* (re)start the DHCP negotiation */
  result = dhcp_discover(netif);
  if (result != ERR_OK) {
    /* free resources allocated above */
    dhcp_stop(netif);
    return ERR_MEM;
  }
  /* Set the flag that says this netif is handled by DHCP. */
  netif->flags |= NETIF_FLAG_DHCP;
  return result;
}

DHCP获取IP地址的4个步骤:discover->offer->request->ack
可以稍微记一下DHCP协议中的报文:

  1. DHCP DISCOVER:客户开始DHCP过程发送的包,是DHCP协议的开始
  2. DHCP OFFER:服务器接收到DISCOVER之后做出的响应,包括给与客户端的IP、客户端的MAC地址、租约过期时间、服务器的识别符等
  3. DHCP REQUEST:客户端对于服务器发出的OFFER的响应,在续约租期的时候也会使用
  4. DHCP ACK:服务器接收到客户端发来的REQUEST之后发出的成功确认的报文,建立连接的时候,客户端接收到这个报文之后才会确认分配给他的IP和其他信息可以被允许使用
  5. DHCP NAKACK相反的报文,表示服务器拒绝了客户端的请求
  6. DHCP RELEASE:一般出现在客户端关机、下线等情况,会使DHCP服务器释放发出此报文的客户端的IP地址
  7. DHCP INFORM:客户端发出的向服务器请求一些信息的报文
  8. DHCP DECLINE:当客户端发现服务器分配的IP地址无法使用时,会发出此报文

那么这里为什么只用了dhcp_discover(netif);这样一个函数呢?
因为这个DHCP的配置啊哈哈哈哈哈~也就是说还没发送数据的那一块,就只做了第一步
所以我们可以看到在dhcp_recv函数中有:

dhcp_handle_offer(netif);
dhcp_handle_ack(netif);

这样的调用情况

LWIP实现DHCP

  1. 定义LWIP_DHCP为1
  2. 调用dhcp_start()启动DHCP
  3. 检测DHCP是否成功
  4. 在while中调用sys_check_timeouts();函数处理内核各种定时事件

DHCP报文格式

代码有删减

struct dhcp{
  /** transaction identifier of last sent request */
  u32_t xid;  // 随机生成的一段字符串,两个数据包拥有相同的xid说明他们属于同一次会话
  /** our connection to the DHCP server */
  struct udp_pcb *pcb;
  /** incoming msg */
  struct dhcp_msg *msg_in;
  /** current DHCP state machine state */
  u8_t state;
  /** retries of current request */
  u8_t tries;
  u8_t subnet_mask_given;

  struct pbuf *p_out; /* pbuf of outcoming msg */
  struct dhcp_msg *msg_out; /* outgoing msg */
  u16_t options_out_len; /* outgoing msg options length */
  u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
  u16_t t1_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
  u16_t t2_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
  ip_addr_t server_ip_addr; /* dhcp server address that offered this lease 提供此租约的dhcp服务器地址*/
  ip_addr_t offered_ip_addr;
  ip_addr_t offered_sn_mask;
  ip_addr_t offered_gw_addr;

  u32_t offered_t0_lease; /* lease period (in seconds)  租约期(以秒为单位)*/
  u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) 建议的续订时间(通常为租赁期的50%)*/
  u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period)  建议的重新绑定时间(通常为租赁期的66%)*/
};

初始化的话基本就是这些了,下篇文章会写echo的实现
这个板子现在有一个问题我记录一下,需要调试,就是用DMA直接进行网口传输的时候,板子的IP地址默认不是192.168.1.10会导致传输错误,但是相同的IP地址在lwip echo这个例程中是可以运行成功的,需要找一下原因了。


下面补充一个概念

SNMP

Simple Network Management Protocol:简答网络管理协议
基于TCP/IP的网络管理包含两个部分:网络管理站(也叫管理进程,manager),和被管的网络单元(也叫被管设备,包括路由器、打印机等等,共同点是都运行TCP/IP协议,)被管设备端与管理相关的软件叫做代理程序或代理进程

管理进程与代理进程之间的通信方式
一是管理进程向代理进程发出请求,询问一个具体的参数值
二是代理进程主动向管理进程报告有某些重要的事情发生

基于TCP/IP的网络管理包含3个组成部分:

  • 管理信息库MIB:包含所有代理进程的所有可被查询和修改的参数
  • 管理信息结构SMI:关于MIB的一套公用的结构和表示符号
  • 管理进程和代理进程之间的通信协议SNMP(SNMP本身可以采用各种协议,只是UDP用的最多TCP/IP协议与lwip库——源代码分析(二)_第3张图片
    管理进程与代理进程之间的交互信息,SNMP定义了5种报文:
  • get - request:从代理进程处提取一个或多个参数值
  • get - next - request:从代理进程处提取一个或多个参数值的下一个参数值
  • set - request:设置代理进程的一个或多个参数值
  • get - response:返回的一个或多个参数值
  • trap:代理进程主动发出,通知管理进程有某些事情发生
    TCP/IP协议与lwip库——源代码分析(二)_第4张图片
    前面3种用的是UDP的161端口trap使用的是UDP的162端口

报文中的具体参数解释

  • 版本信息:0代表SNMPv1
  • PDU类型
    TCP/IP协议与lwip库——源代码分析(二)_第5张图片
  • 差错状态字段
    TCP/IP协议与lwip库——源代码分析(二)_第6张图片

你可能感兴趣的:(TCP/IP协议栈,网络通信,tcpip,网络接口)