void do_ndisc_update_from_reply_packet()

in src/iccpd/src/iccp_ifm.c [1018:1258]


void do_ndisc_update_from_reply_packet(unsigned int ifindex, char *ipv6_addr, uint8_t mac_addr[ETHER_ADDR_LEN])
{
    struct System *sys = NULL;
    struct CSM *csm = NULL;
    struct Msg *msg = NULL;
    struct NDISCMsg *ndisc_msg = NULL, *ndisc_info = NULL;
    struct VLAN_ID *vlan_id_list = NULL;
    struct Msg *msg_send = NULL;
    char mac_str[18] = "";
    uint8_t null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    uint16_t vid = 0;
    int err = 0, ln = 0;
    struct LocalInterface *peer_link_if = NULL;
    int is_link_local = 0;

    char buf[MAX_BUFSIZE] = { 0 };
    size_t msg_len = 0;
    char addr_null[16] = { 0 };
    uint16_t vlan_id = 0;
    struct VLAN_ID vlan_key = { 0 };

    struct LocalInterface *lif_po = NULL, *ndisc_lif = NULL;

    int verify_ndisc = 0;

    if (!(sys = system_get_instance()))
        return;

    /* Find local itf */
    if (!(ndisc_lif = local_if_find_by_ifindex(ifindex)))
        return;

    sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

    /* create Ndisc msg */
    msg_len = sizeof(struct NDISCMsg);
    ndisc_msg = (struct NDISCMsg *)&buf;
    ndisc_msg->op_type = NEIGH_SYNC_LIF;
    sprintf(ndisc_msg->ifname, "%s", ndisc_lif->name);
    memcpy((char *)ndisc_msg->ipv6_addr, ipv6_addr, 16);
    memcpy(ndisc_msg->mac_addr, mac_addr, ETHER_ADDR_LEN);

    ICCPD_LOG_DEBUG(__FUNCTION__, "nd ifindex [%d] (%s) ip %s mac %s", ifindex, ndisc_lif->name, show_ipv6_str(ipv6_addr), mac_str);

    if (memcmp(ndisc_lif->ipv6_addr, addr_null, 16) == 0) {
        ICCPD_LOG_DEBUG(__FUNCTION__, "IPv6 address not configured on %s, ignore ND", ndisc_lif->name);
        return;
    }

    uint8_t bcast_mac[ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    if (memcmp(ndisc_msg->mac_addr, bcast_mac, ETHER_ADDR_LEN) == 0)
    {
        ICCPD_LOG_DEBUG(__FUNCTION__, "Ignoring neighbor entry due to bcast lladdr");
        return;
    }

    if ((strncmp(ndisc_lif->name, VLAN_PREFIX, strlen(VLAN_PREFIX)) == 0)) {
        sscanf (ndisc_lif->name, "Vlan%hu", &vlan_id);
    }

    if (vlan_id) {
        memset(&vlan_key, 0, sizeof(struct VLAN_ID));
        vlan_key.vid = vlan_id;
    }
    /* Find MLACP itf, member of port-channel */
    LIST_FOREACH(csm, &(sys->csm_list), next)
    {
        vid = 0;
        LIST_FOREACH(lif_po, &(MLACP(csm).lif_list), mlacp_next)
        {
            if (lif_po->type != IF_T_PORT_CHANNEL) {
                ln = __LINE__;
                continue;
            }

            vid = 0;
            if (!local_if_is_l3_mode(lif_po))
            {
                /* Is the L2 MLAG itf belong to a vlan?*/
                vlan_id_list = RB_FIND(vlan_rb_tree, &(lif_po->vlan_tree), &vlan_key);

                if (!vlan_id_list) {
                    ln = __LINE__;
                    continue;
                }

                if (!vlan_id_list->vlan_itf) {
                    ln = __LINE__;
                    continue;
                }

                if (vlan_id_list->vlan_itf->ifindex != ifindex) {
                    ln = __LINE__;
                    continue;
                }

                vid = vlan_id_list->vid;
            }
            else
            {
                /* Is the ND belong to a L3 mode MLAG itf? */
                if (ifindex != lif_po->ifindex) {
                    ln = __LINE__;
                    continue;
                }
                ICCPD_LOG_DEBUG(__FUNCTION__, "ND is from intf %s", lif_po->name);
            }

            verify_ndisc = 1;

            break;
        }

        if (lif_po) {
            break;
        } else if (csm->peer_link_if) {
           peer_link_if = csm->peer_link_if;
           if (!local_if_is_l3_mode(peer_link_if)) {
               vid = 0;
               vlan_id_list = RB_FIND(vlan_rb_tree, &(peer_link_if->vlan_tree), &vlan_key);

               if (vlan_id_list && vlan_id_list->vlan_itf) {
                   if (vlan_id_list->vlan_itf->ifindex == ifindex) {
                       vid = vlan_id_list->vid;
                       ICCPD_LOG_DEBUG(__FUNCTION__, "ND is from peer link vlan %d", vid);
                       verify_ndisc = 1;
                       lif_po = peer_link_if;
                       break;
                   }
               }
           }
        }
    }

    if (!(csm && lif_po)) {
        ICCPD_LOG_DEBUG(__FUNCTION__, "ND received no PO ln %d", ln);
        return;
    }
    if (!verify_ndisc) {
        ICCPD_LOG_DEBUG(__FUNCTION__, "ND received no verify_ndisc ln %d", ln);
        return;
    }

    if ((memcmp(show_ipv6_str((char *)ndisc_msg->ipv6_addr), "FE80", 4) == 0)
            || (memcmp(show_ipv6_str((char *)ndisc_msg->ipv6_addr), "fe80", 4) == 0))
    {
        is_link_local = 1;
    }

    if (vid && vlan_id_list && vlan_id_list->vlan_itf) {
        if (memcmp((char *)ndisc_msg->ipv6_addr, (char *)vlan_id_list->vlan_itf->ipv6_addr, 16) == 0)
        {
            ICCPD_LOG_DEBUG(__FUNCTION__, "Ignoring neighbor entry for My Ip %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
            return;
        }

        if (is_link_local)
        {
            if (memcmp((char *)ndisc_msg->ipv6_addr, (char *)vlan_id_list->vlan_itf->ipv6_ll_addr, 16) == 0)
            {
                ICCPD_LOG_DEBUG(__FUNCTION__, "Ignoring neighbor entry for My Ip %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
                return;
            }
        }
    }

    /* update lif ND */
    TAILQ_FOREACH(msg, &MLACP(csm).ndisc_list, tail)
    {
        ndisc_info = (struct NDISCMsg *)msg->buf;

        if (memcmp((char *)ndisc_info->ipv6_addr, (char *)ndisc_msg->ipv6_addr, 16) != 0)
            continue;

        /* If MAC addr is NULL, use the old one */
        if (memcmp(mac_addr, null_mac, ETHER_ADDR_LEN) == 0)
        {
            memcpy(ndisc_msg->mac_addr, ndisc_info->mac_addr, ETHER_ADDR_LEN);
            sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", ndisc_info->mac_addr[0], ndisc_info->mac_addr[1],
                    ndisc_info->mac_addr[2], ndisc_info->mac_addr[3], ndisc_info->mac_addr[4], ndisc_info->mac_addr[5]);
        }

        /* update ND */
        if (ndisc_info->op_type != ndisc_msg->op_type
            || strcmp(ndisc_info->ifname, ndisc_msg->ifname) != 0 || memcmp(ndisc_info->mac_addr, ndisc_msg->mac_addr, ETHER_ADDR_LEN) != 0)
        {
            ndisc_info->op_type = ndisc_msg->op_type;
            sprintf(ndisc_info->ifname, "%s", ndisc_msg->ifname);
            memcpy(ndisc_info->mac_addr, ndisc_msg->mac_addr, ETHER_ADDR_LEN);
             ICCPD_LOG_DEBUG(__FUNCTION__, "Update ND for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr)); 
        }
        break;
    }

    /* enquene lif_msg (add) */
    if (!msg)
    {
        /* If MAC addr is NULL, and same ipv6 item is not exist in ndisc_list */
        if (memcmp(mac_addr, null_mac, ETHER_ADDR_LEN) == 0)
        {
            return;
        }

        ndisc_msg->op_type = NEIGH_SYNC_LIF;
        ndisc_msg->learn_flag = NEIGH_LOCAL;
        if (iccp_csm_init_msg(&msg, (char *)ndisc_msg, msg_len) == 0)
        {
            mlacp_enqueue_ndisc(csm, msg);
            /* ICCPD_LOG_DEBUG(__FUNCTION__, "NDISC-list enqueue: %s, add %s", ndisc_msg->ifname, show_ipv6_str((char *)ndisc_msg->ipv6_addr)); */
        }
        else
            ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue NDISC-list: %s, add %s", ndisc_msg->ifname, show_ipv6_str((char *)ndisc_msg->ipv6_addr));
    }

    ICCPD_LOG_DEBUG(__FUNCTION__, "add nd entry(%s, %s, %s) to kernel",
            ndisc_msg->ifname, show_ipv6_str((char *)ndisc_msg->ipv6_addr), mac_str);
    if ((err = iccp_netlink_neighbor_request(AF_INET6, (uint8_t *)ndisc_msg->ipv6_addr, 1, ndisc_msg->mac_addr, ndisc_msg->ifname, 0, 3)) < 0)
    {
        if (err != ICCP_NLE_SEQ_MISMATCH) {
            ICCPD_LOG_NOTICE(__FUNCTION__, "Failed to add nd entry(%s, %s, %s) to kernel, status %d",
                        ndisc_msg->ifname, show_ipv6_str((char *)ndisc_msg->ipv6_addr), mac_str, err);
            return;
        }
    }

    /* enqueue iccp_msg (add) */
    if (MLACP(csm).current_state == MLACP_STATE_EXCHANGE)
    {
        ndisc_msg->op_type = NEIGH_SYNC_ADD;
        ndisc_msg->flag = 0;
        if (iccp_csm_init_msg(&msg_send, (char *)ndisc_msg, msg_len) == 0)
        {
            TAILQ_INSERT_TAIL(&(MLACP(csm).ndisc_msg_list), msg_send, tail);
            /* ICCPD_LOG_DEBUG(__FUNCTION__, "Enqueue ND[ADD] for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr)); */
        }
        else
            ICCPD_LOG_WARN(__FUNCTION__, "Failed to enqueue ND[ADD] message for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
    }

    return;
}