您的位置:首页 > 运维架构

openlldp-0.4alpha实现详解(四)——rx_sm模块

2014-02-19 12:38 609 查看
该模块完成接收状态机的跳转以及接收帧所进行的相关处理。
lldp的接收状态机,参见lldp标准文档。该实现代码和标准文档基本一致,变量名和函数名都大部分相同。

int rxProcessFrame(struct lldp_port *lldp_port) {

…
…

/*
主要是验证报文的正确性:具体要验证报文的目的地址以及报文类型字段
*/
/* As per section 10.3.1, verify the destination and ethertype */
    expect_hdr.dst[0] = 0x01;
    expect_hdr.dst[1] = 0x80;
    expect_hdr.dst[2] = 0xc2;
    expect_hdr.dst[3] = 0x00;
    expect_hdr.dst[4] = 0x00;
    expect_hdr.dst[5] = 0x0e;
    expect_hdr.ethertype = htons(0x88cc);

    ether_hdr = (struct eth_hdr *)&lldp_port->rx.frame[0];

    debug_printf(DEBUG_INT, "LLPDU Dst: ");
    debug_hex_printf(DEBUG_INT, (uint8_t *)ether_hdr->dst, 6);

    debug_printf(DEBUG_EXCESSIVE, "Expect Dst: ");
    debug_hex_printf(DEBUG_EXCESSIVE, (uint8_t *)expect_hdr.dst, 6);

    /* Validate the frame's destination */
    if(
            ether_hdr->dst[0] != expect_hdr.dst[0] ||
            ether_hdr->dst[1] != expect_hdr.dst[1] ||
            ether_hdr->dst[2] != expect_hdr.dst[2] ||
            ether_hdr->dst[3] != expect_hdr.dst[3] ||
            ether_hdr->dst[4] != expect_hdr.dst[4] ||
            ether_hdr->dst[5] != expect_hdr.dst[5] ) {

        debug_printf(DEBUG_NORMAL, "[ERROR] This frame is incorrectly addressed to: ");
        debug_hex_printf(DEBUG_NORMAL, (uint8_t *)ether_hdr->dst, 6);
        debug_printf(DEBUG_NORMAL, "[ERROR] This frame should be addressed to: ");
        debug_hex_printf(DEBUG_NORMAL, (uint8_t *)expect_hdr.dst, 6);
        debug_printf(DEBUG_NORMAL, "[ERROR] statsFramesInTotal will *NOT* be incremented\n");

        badFrame++;
    }

    debug_printf(DEBUG_INT, "LLPDU Src: ");
    debug_hex_printf(DEBUG_INT, (uint8_t *)ether_hdr->src, 6);

    debug_printf(DEBUG_INT, "LLPDU Ethertype: %x\n", htons(ether_hdr->ethertype));

    debug_printf(DEBUG_EXCESSIVE, "Expect Ethertype: %x\n", htons(expect_hdr.ethertype));

    /* Validate the frame's ethertype */
    if(ether_hdr->ethertype != expect_hdr.ethertype) {
        debug_printf(DEBUG_NORMAL, "[ERROR] This frame has an incorrect ethertype of: '%x'.\n", htons(ether_hdr->ethertype));

        badFrame++;
    }

    if(!badFrame) {
        lldp_port->rx.statistics.statsFramesInTotal ++;
    }

…
…

/*
请注意lldp报文TLV的格式,前7个bits为tlv类型字段,后9个位数据长度字段。
*/
/* Grab the first 9 bits */
        tlv_length = htons(*tlv_hdr) & 0x01FF;

        /* Then shift to get the last 7 bits */
        tlv_type = htons(*tlv_hdr) >> 9;

/*
lldp报文中tlv最少为4个,分别为Chasis ID TLV、Port ID TLV、TTL TLV、End TLV
*/
        /* Validate as per 802.1AB section 10.3.2*/
        if(num_tlvs <= 3) {
            if(num_tlvs != tlv_type) {
                debug_printf(DEBUG_NORMAL, "[ERROR] TLV number %d should have tlv_type %d, but is actually %d\n", num_tlvs, num_tlvs, tlv_type);
                debug_printf(DEBUG_NORMAL, "[ERROR] statsFramesDiscardedTotal and statsFramesInErrorsTotal will be incremented as per 802.1AB 10.3.2\n");
                lldp_port->rx.statistics.statsFramesDiscardedTotal++;
                lldp_port->rx.statistics.statsFramesInErrorsTotal++;
                badFrame++;
            }
        }
/*
缓存lldp报文中tlv的值
*/
tlv->type        = tlv_type;
tlv->length      = tlv_length;
	if(tlv->length > 0)	  
	  tlv->info_string = calloc(1, tlv_length);
/*
如果LLDP中的tlv为TTL,那么则更新rx.timers.rxTTL的值
*/
  if(tlv_type == TIME_TO_LIVE_TLV) {
	  if(tlv_length != 2) {
                debug_printf(DEBUG_NORMAL, "[ERROR] TTL TLV has an invalid length!  Should be '2', but is '%d'\n", tlv_length);
#ifndef WIN32
#warning We should actually discard this frame and print out an error...
#warning Write a unit test to stress this
#endif // WIN32
            } else {
                lldp_port->rx.timers.rxTTL = htons(*(uint16_t *)&tlv_info_string[0]);
		msap_ttl_tlv = tlv;
                debug_printf(DEBUG_EXCESSIVE, "rxTTL is: %d\n", lldp_port->rx.timers.rxTTL);
            }
        }

        if(tlv->info_string) {
            memset(tlv->info_string, 0x0, tlv_length);
            memcpy(tlv->info_string, tlv_info_string, tlv_length);
        } 

        /* Validate the TLV */
        if(validate_tlv[tlv_type] != NULL) {
            debug_printf(DEBUG_EXCESSIVE, "Found a validator for TLV type %d.\n", tlv_type);

            debug_hex_dump(DEBUG_EXCESSIVE, tlv->info_string, tlv->length);

            if(validate_tlv[tlv_type](tlv) != XVALIDTLV) {
                badFrame++;
            }
        } else {
	  // NOTE: Any organizationally specific TLVs should get processed through validate_generic_tlv
            debug_printf(DEBUG_EXCESSIVE, "Didn't find specific validator for TLV type %d.  Using validate_generic_tlv.\n", tlv_type);
            if(validate_generic_tlv(tlv) != XVALIDTLV) {
                badFrame++;
            }
        }
…
…
/*
将之前缓存的lldp报文中tlv加入到tlv_list中
*/
	cached_tlv = initialize_tlv();

	if(tlvcpy(cached_tlv, tlv) != 0) {
	  debug_printf(DEBUG_TLV, "Error copying TLV for MSAP cache!\n");
	  } 

	debug_printf(DEBUG_EXCESSIVE, "Adding exploded TLV to MSAP TLV list.\n");
	// Now we can start stuffing the msap data... ;)
	add_tlv(cached_tlv, &tlv_list);
/*
 如果是CHASSIS_ID_TLV和PORT_ID_TLV,那么则缓存它们的值。并将它们拼接为msap_id
*/
	if(tlv_type == CHASSIS_ID_TLV) {
	  debug_printf(DEBUG_NORMAL, "Copying TLV1 for MSAP Processing...\n");
	  msap_tlv1 = initialize_tlv();
	  tlvcpy(msap_tlv1, tlv);
	} else if(tlv_type == PORT_ID_TLV) {
	  debug_printf(DEBUG_NORMAL, "Copying TLV2 for MSAP Processing...\n");
	  msap_tlv2 = initialize_tlv();
	  tlvcpy(msap_tlv2, tlv);

	  
	  //Minus 2, for the chassis id subtype and port id subtype... 
	  // IEEE 802.1AB specifies that the MSAP shall be composed of 
	  // The value of the subtypes. 
	  msap_id = calloc(1, msap_tlv1->length - 1  + msap_tlv2->length - 1);

	  if(msap_id == NULL)
	    {
	      debug_printf(DEBUG_NORMAL, "[ERROR] Unable to malloc buffer in %s() at line: %d!\n", __FUNCTION__, __LINE__);
	    }
	  
	  // Copy the first part of the MSAP 
	  memcpy(msap_id, &msap_tlv1->info_string[1], msap_tlv1->length - 1);
	  
	  // Copy the second part of the MSAP 
	  memcpy(&msap_id[msap_tlv1->length - 1], &msap_tlv2->info_string[1], msap_tlv2->length - 1);
	  
	  msap_length = (msap_tlv1->length - 1) + (msap_tlv2->length - 1);
	  
	  debug_printf(DEBUG_MSAP, "MSAP TLV1 Length: %d\n", msap_tlv1->length);
	  debug_printf(DEBUG_MSAP, "MSAP TLV2 Length: %d\n", msap_tlv2->length);
	  
	  debug_printf(DEBUG_MSAP, "MSAP is %d bytes: ", msap_length);
	  debug_hex_printf(DEBUG_MSAP, msap_id, msap_length);
	  debug_hex_dump(DEBUG_MSAP, msap_id, msap_length);

	  // Free the MSAP pieces
	  destroy_tlv(&msap_tlv1);
	  destroy_tlv(&msap_tlv2);
	  
	  msap_tlv1 = NULL;
	  msap_tlv2 = NULL;
	  	  
/* 指示有新的邻居信息到来*/
	  have_msap = 1;
	  }
…
…
if(have_msap)
      {
#ifndef WIN32
        #warning We need to verify whether this is actually the case.
#endif // WIN32
	lldp_port->rxChanges = TRUE;
	
	debug_printf(DEBUG_TLV, "We have a(n) %d byte MSAP!\n", msap_length);
/*
创建一条新的保存邻居信息的lldp_msap结构体,并将之前缓存的tlv信息复制到其中。然后更新该端口对应的lldp_port结构体中的lldp_msap信息。亦即更新该底层端口对应的邻居信息。
*/
	msap_cache = calloc(1, sizeof(struct lldp_msap));

	msap_cache->id = msap_id;
	msap_cache->length = msap_length;
	msap_cache->tlv_list = tlv_list;
	msap_cache->next = NULL;

	msap_cache->ttl_tlv = msap_ttl_tlv;
	msap_ttl_tlv = NULL;

	//debug_printf(DEBUG_MSAP, "Iterating MSAP Cache...\n");

	//iterate_msap_cache(msap_cache);

	//debug_printf(DEBUG_MSAP, "Updating MSAP Cache...\n");

	debug_printf(DEBUG_MSAP, "Setting rxInfoTTL to: %d\n", lldp_port->rx.timers.rxTTL);

	msap_cache->rxInfoTTL = lldp_port->rx.timers.rxTTL;

	update_msap_cache(lldp_port, msap_cache);

	if(msap_tlv1 != NULL) {
	  debug_printf(DEBUG_NORMAL, "Error: msap_tlv1 is still allocated!\n");
	  free(msap_tlv1);
	  msap_tlv1 = NULL;
	}

	if(msap_tlv2 != NULL) {
	  debug_printf(DEBUG_NORMAL, "Error: msap_tlv2 is still allocated!\n");
	  free(msap_tlv2);
	  msap_tlv2 = NULL;
	}

      }
    else
      {
	debug_printf(DEBUG_NORMAL, "[ERROR] No MSAP for TLVs in Frame!\n");
      }

    /* Report frame errors */
    if(badFrame) {
        rxBadFrameInfo(badFrame);
    }

    return badFrame;
}


rxProcessFrame主要是提取了报文中的tlv,解析为对应的lldp_tlv节点,并缓存相应的信息,最终构造lldp_msap结构体。并更新邻居信息。而在update_msap_cache函数中,会判断rxProcessFrame函数构造的lldp_msap在本端口的lldp_msap链表中是否存在。若存在,那么直接进行替换(不检查是否完全完全相等,简便的做法)。若不存在,则说明是一个新邻居,那么完成邻居信息结构体lldp_msap的链表插入工作。

在lldp报文中tlv被组织为7bit的type字段,9bit的length字段。这种组织方式,在存储tlv时,极为不便。这里将这种组织方式转化为lldp_tlv的组织方式。type和length都可以使用现有的数据类型表示,方便程序的编写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: