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

来源:互联网 发布:mac 屏幕睡眠时间 编辑:程序博客网 时间:2024/05/16 07:07
    该模块完成接收状态机的跳转以及接收帧所进行的相关处理。

    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 // WIN32lldp_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都可以使用现有的数据类型表示,方便程序的编写。


0 0
原创粉丝点击