int mlacp_fsm_update_ndisc_entry()

in src/iccpd/src/mlacp_sync_update.c [924:1225]


int mlacp_fsm_update_ndisc_entry(struct CSM *csm, struct NDISCMsg *ndisc_entry)
{
    struct Msg *msg = NULL;
    struct NDISCMsg *ndisc_msg = NULL, ndisc_data;
    struct LocalInterface *local_if;
    struct LocalInterface *vlan_if = NULL;
    struct LocalInterface *peer_link_if = NULL;
    struct LocalInterface *local_vlan_if = NULL;
    struct VLAN_ID *vlan_id_list = NULL;
    int set_ndisc_flag = 0;
    char mac_str[18] = "";
    int my_ip_nd_flag = 0;
    int vlan_count = 0;
    int err = 0, ln = 0;
    int permanent_neigh = 0;
    int is_ack_ll = 0;
    int is_link_local = 0;
    uint16_t vlan_id = 0;
    struct VLAN_ID vlan_key = { 0 };
    int vid_intf_present = 0;

    if (!csm || !ndisc_entry)
        return MCLAG_ERROR;

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

    ICCPD_LOG_DEBUG(__FUNCTION__,
           "Received ND Info, intf[%s] Flag %x, IP[%s], MAC[%s]",
           ndisc_entry->ifname, ndisc_entry->flag, show_ipv6_str((char *)ndisc_entry->ipv6_addr), mac_str);

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

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

    if (vlan_id)
    {
        memset(&vlan_key, 0, sizeof(struct VLAN_ID));
        vlan_key.vid = vlan_id;

        peer_link_if = local_if_find_by_name(csm->peer_itf_name);

        if (peer_link_if && !local_if_is_l3_mode(peer_link_if))
        {
            ln = __LINE__;
            /* Is peer-linlk itf belong to a vlan the same as peer? */
            vlan_id_list = RB_FIND(vlan_rb_tree, &(peer_link_if->vlan_tree), &vlan_key);

            if (vlan_id_list)
            {
                vlan_count++;
                if (vlan_id_list->vlan_itf) {
                    if (strcmp(vlan_id_list->vlan_itf->name, ndisc_entry->ifname) == 0) {
                        ln = __LINE__;
                        vid_intf_present = 1;
                    }

                    if (vid_intf_present && local_if_is_l3_mode(vlan_id_list->vlan_itf)) {
                        if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)vlan_id_list->vlan_itf->ipv6_addr, 16) == 0)
                        {
                            my_ip_nd_flag = 1;
                        }

                        if ((my_ip_nd_flag == 0) && is_link_local)
                        {
                            if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)vlan_id_list->vlan_itf->ipv6_ll_addr, 16) == 0)
                            {
                                my_ip_nd_flag = 1;
                            }
                        }
                    }

                    ICCPD_LOG_DEBUG(__FUNCTION__,
                            "ND is learnt from intf %s, peer-link %s is the member of this vlan",
                            vlan_id_list->vlan_itf->name, peer_link_if->name);

                    /* Peer-link belong to L3 vlan is alive, set the NDISC info */
                    set_ndisc_flag = 1;
                }
            }

            ICCPD_LOG_DEBUG(__FUNCTION__, "ND Received ln %d, vlan_count %d, set_ndisc_flag %d, my_ip %d",
                    ln, vlan_count, set_ndisc_flag, my_ip_nd_flag);

            if (vlan_count == 0)
            {
                vlan_if = local_if_find_by_name(ndisc_entry->ifname);
                if (vlan_if && vlan_if->is_l3_proto_enabled)
                {
                    if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)vlan_if->ipv6_addr, 16) == 0)
                    {
                        my_ip_nd_flag = 1;
                    }

                    if ((my_ip_nd_flag == 0) && is_link_local)
                    {
                        if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)vlan_if->ipv6_ll_addr, 16) == 0)
                        {
                            my_ip_nd_flag = 1;
                        }
                    }
                    set_ndisc_flag = 1;
                }
            }
        }
    }

    if(my_ip_nd_flag)
    {
        ICCPD_LOG_DEBUG(__FUNCTION__," ignoring ND sync for self ipv6 %s ", show_ipv6_str((char *)ndisc_entry->ipv6_addr));
        return 0;
    }

    if (set_ndisc_flag == 0)
    {
        LIST_FOREACH(local_if, &(MLACP(csm).lif_list), mlacp_next)
        {
            if (local_if->type == IF_T_PORT_CHANNEL)
            {
                if (!local_if_is_l3_mode(local_if))
                {
                    ln = __LINE__;
                    /* Is the L2 MLAG itf belong to a vlan the same as peer? */
                    if (vlan_id) {
                        vlan_id_list = RB_FIND(vlan_rb_tree, &(local_if->vlan_tree), &vlan_key);
                        if (vlan_id_list && vlan_id_list->vlan_itf) {

                            if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)vlan_id_list->vlan_itf->ipv6_addr, 16) == 0)
                            {
                                my_ip_nd_flag = 1;
                            }

                            ICCPD_LOG_DEBUG(__FUNCTION__, "ND is learnt from intf %s, %s is the member of this vlan, my_ip %d",
                                    vlan_id_list->vlan_itf->name, local_if->name, my_ip_nd_flag);
                        }
                    }

                    ICCPD_LOG_DEBUG(__FUNCTION__, "ND received PO %s, active %d, ln %d",
                            local_if->name, local_if->po_active, ln);
                    if (vlan_id_list && local_if->po_active == 1)
                    {
                        /* Any po of L3 vlan is alive, set the NDISC info */
                        set_ndisc_flag = 1;
                        break;
                    }
                }
                else
                {
                    /* Is the ARP belong to a L3 mode MLAG itf? */
                    if (strcmp(local_if->name, ndisc_entry->ifname) == 0)
                    {
                        ICCPD_LOG_DEBUG(__FUNCTION__, "ND is learnt from mclag L3 intf %s, active %d",
                                local_if->name, local_if->po_active);
                        if (local_if->po_active == 1)
                        {
                            /* po is alive, set the NDISC info */
                            set_ndisc_flag = 1;
                            break;
                        }
                    }
                    else
                    {
                        ln = __LINE__;
                        continue;
                    }
                }
            }
        }
    }

    if(my_ip_nd_flag)
    {
        ICCPD_LOG_DEBUG(__FUNCTION__," ignoring ND sync for self ipv6 %s ", show_ipv6_str((char *)ndisc_entry->ipv6_addr));
        return 0;
    }
    /* set dynamic Ndisc */
    if (set_ndisc_flag == 1)
    {
        if (ndisc_entry->flag & NEIGH_SYNC_FLAG_SELF_LL)
        {
            permanent_neigh = 1;
            is_ack_ll = 1;
        }

        if (ndisc_entry->flag & NEIGH_SYNC_FLAG_SELF_IP)
        {
            permanent_neigh = 1;
        }

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

           if (ndisc_entry->flag & NEIGH_SYNC_FLAG_ACK)
           {
               ICCPD_LOG_DEBUG(__FUNCTION__,"Sync ND on ACK ");
               syn_ack_local_neigh_mac_info_to_peer(ndisc_entry->ifname, is_ack_ll);
           }
        }
        else
        {
            err = iccp_netlink_neighbor_request(AF_INET6, (uint8_t *)ndisc_entry->ipv6_addr, 0, ndisc_entry->mac_addr, ndisc_entry->ifname, permanent_neigh, 11);
            if (err < 0)
            {
                if (err != ICCP_NLE_SEQ_MISMATCH) {
                    ICCPD_LOG_NOTICE(__FUNCTION__, "Failed to delete nd entry(%s %s %s) from kernel status %d",
                                ndisc_entry->ifname, show_ipv6_str((char *)ndisc_entry->ipv6_addr), mac_str, err);
                    return MCLAG_ERROR;
                }
            }
        }

        ICCPD_LOG_DEBUG(__FUNCTION__, "NDISC update for %s %s %s", ndisc_entry->ifname, show_ipv6_str((char *)ndisc_entry->ipv6_addr), mac_str);
    }
    else
    {
        ICCPD_LOG_NOTICE(__FUNCTION__, "Failure: port-channel is not alive, ln %d", ln);
        /* TODO Set static route through peer-link or just skip it? */
        local_vlan_if = local_if_find_by_name(ndisc_entry->ifname);
        if (local_vlan_if) {
            if (memcmp((char *)ndisc_entry->ipv6_addr, (char *)local_vlan_if->ipv6_addr, 16) == 0) {
                ICCPD_LOG_DEBUG(__FUNCTION__, "ignore my ip %s", show_ipv6_str((char *)ndisc_entry->ipv6_addr));
                return 0;
            }
        }
    }

    /* update NDISC list */
    TAILQ_FOREACH(msg, &(MLACP(csm).ndisc_list), tail)
    {
        ndisc_msg = (struct NDISCMsg *)msg->buf;
        if (memcmp((char *)ndisc_msg->ipv6_addr, (char *)ndisc_entry->ipv6_addr, 16) == 0)
        {
            /* ndisc_msg->op_type = tlv->type; */
            sprintf(ndisc_msg->ifname, "%s", ndisc_entry->ifname);
            memcpy(ndisc_msg->mac_addr, ndisc_entry->mac_addr, ETHER_ADDR_LEN);
            break;
        }
    }

    /* delete/add NDISC list */
    if (msg && ndisc_entry->op_type == NEIGH_SYNC_DEL)
    {
        TAILQ_REMOVE(&(MLACP(csm).ndisc_list), msg, tail);
        free(msg->buf);
        free(msg);
        /* ICCPD_LOG_INFO(__FUNCTION__, "Del ndisc queue successfully"); */
    }
    else if (!msg && ndisc_entry->op_type == NEIGH_SYNC_ADD)
    {
        ndisc_msg = (struct NDISCMsg *)&ndisc_data;
        sprintf(ndisc_msg->ifname, "%s", ndisc_entry->ifname);
        memcpy((char *)ndisc_msg->ipv6_addr, (char *)ndisc_entry->ipv6_addr, 16);
        ndisc_msg->op_type = ndisc_entry->op_type;
        ndisc_msg->flag = 0;
        ndisc_msg->learn_flag = NEIGH_REMOTE;
        memcpy(ndisc_msg->mac_addr, ndisc_entry->mac_addr, ETHER_ADDR_LEN);
        if (iccp_csm_init_msg(&msg, (char *)ndisc_msg, sizeof(struct NDISCMsg)) == 0)
        {
            mlacp_enqueue_ndisc(csm, msg);
            /* ICCPD_LOG_INFO(__FUNCTION__, "Add ndisc queue successfully"); */
        }
    }

    /* remove all NDISC msg queue, when receive peer's NDISC list at the same time */
    TAILQ_FOREACH(msg, &(MLACP(csm).ndisc_msg_list), tail)
    {
        ndisc_msg = (struct NDISCMsg *)msg->buf;
        if (memcmp((char *)ndisc_msg->ipv6_addr, (char *)ndisc_entry->ipv6_addr, 16) == 0)
            break;
    }

    while (msg)
    {
        ndisc_msg = (struct NDISCMsg *)msg->buf;
        TAILQ_REMOVE(&(MLACP(csm).ndisc_msg_list), msg, tail);
        free(msg->buf);
        free(msg);
        TAILQ_FOREACH(msg, &(MLACP(csm).ndisc_msg_list), tail)
        {
            ndisc_msg = (struct NDISCMsg *)msg->buf;
            if (memcmp((char *)ndisc_msg->ipv6_addr, (char *)ndisc_entry->ipv6_addr, 16) == 0)
                break;
        }
    }

    return 0;
}