static void do_ndisc_learn_from_kernel()

in src/iccpd/src/iccp_ifm.c [375:643]


static void do_ndisc_learn_from_kernel(struct ndmsg *ndm, struct rtattr *tb[], int msgtype, int is_del)
{
    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;
    uint16_t vid = 0;
    int entry_exists = 0;
    int is_link_local = 0;
    int ln = 0;
    uint16_t vlan_id = 0;
    struct VLAN_ID vlan_key = { 0 };

    char buf[MAX_BUFSIZE] = { 0 };
    size_t msg_len = 0;
    char addr_null[16] = { 0 };

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

    int verify_neigh = 0;
    int neigh_update = 0;

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

    /* Find local itf */
    if (!(ndisc_lif = local_if_find_by_ifindex(ndm->ndm_ifindex)))
        return;

    /* 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);
    if (tb[NDA_DST])
        memcpy(&ndisc_msg->ipv6_addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
    if (!is_del && tb[NDA_LLADDR])
        memcpy(ndisc_msg->mac_addr, RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR]));

    ICCPD_LOG_NOTICE(__FUNCTION__, "ndisc type %s, state (%04X)(%d), ifindex [%d] (%s), ip %s, mac [%02X:%02X:%02X:%02X:%02X:%02X]",
                    msgtype == RTM_NEWNEIGH ? "New" : "Del", ndm->ndm_state, fwd_neigh_state_valid(ndm->ndm_state),
                    ndm->ndm_ifindex, ndisc_lif->name,
                    show_ipv6_str((char *)ndisc_msg->ipv6_addr),
                    ndisc_msg->mac_addr[0], ndisc_msg->mac_addr[1], ndisc_msg->mac_addr[2], ndisc_msg->mac_addr[3], ndisc_msg->mac_addr[4],
                    ndisc_msg->mac_addr[5]);


    if (msgtype != RTM_DELNEIGH) {
        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");
        msgtype = RTM_DELNEIGH;
        return;
    }

    if ((strncmp(ndisc_msg->ifname, VLAN_PREFIX, strlen(VLAN_PREFIX)) == 0)) {
        sscanf (ndisc_msg->ifname, "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;
        ln = __LINE__;
        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 != ndm->ndm_ifindex) {
                    ln = __LINE__;
                    continue;
                }

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

                ICCPD_LOG_DEBUG(__FUNCTION__, "neighbor is from intf %s", lif_po->name);
            }

            verify_neigh = 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 == ndm->ndm_ifindex) {
                       vid = vlan_id_list->vid;
                       ICCPD_LOG_DEBUG(__FUNCTION__, "ND is from peer link vlan %d", vid);
                       verify_neigh = 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_neigh) {
        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(&ndisc_info->ipv6_addr, &ndisc_msg->ipv6_addr, 16) != 0)
            continue;

        entry_exists = 1;
        if (msgtype == RTM_DELNEIGH)
        {
            /* delete ND */
            TAILQ_REMOVE(&MLACP(csm).ndisc_list, msg, tail);
            free(msg->buf);
            free(msg);
            msg = NULL;
            ICCPD_LOG_DEBUG(__FUNCTION__, "Delete neighbor %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
        }
        else
        {
            /* update ND */
            if (ndisc_info->op_type != ndisc_info->op_type
                || strcmp(ndisc_info->ifname, ndisc_info->ifname) != 0 || memcmp(ndisc_info->mac_addr, ndisc_info->mac_addr, ETHER_ADDR_LEN) != 0)
            {
                neigh_update = 1;
                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 neighbor for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
            }
        }
        break;
    }

    if (msg && !neigh_update)
        return;

    if (msgtype != RTM_DELNEIGH)
    {
        /* enquene lif_msg (add) */
        if (!msg)
        {
            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_DEBUG(__FUNCTION__, "Failed to enqueue Ndisc-list: %s, add %s",
                                ndisc_msg->ifname, show_ipv6_str((char *)ndisc_msg->ipv6_addr));
        }

        /* 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 Ndisc[ADD] for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr)); */
            }
            else
                ICCPD_LOG_DEBUG(__FUNCTION__, "Failed to enqueue Ndisc[ADD] message for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
        }
    }
    else
    {
        /* enqueue iccp_msg (delete) */
        /* send delete notification, only if entry is present in DB*/
        if ((MLACP(csm).current_state == MLACP_STATE_EXCHANGE) && entry_exists)
        {
            ndisc_msg->op_type = NEIGH_SYNC_DEL;
            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 Ndisc[DEL] for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr)); */
            }
            else
                ICCPD_LOG_DEBUG(__FUNCTION__, "Failed to enqueue Ndisc[DEL] message for %s", show_ipv6_str((char *)ndisc_msg->ipv6_addr));
        } else {
            ICCPD_LOG_DEBUG(__FUNCTION__, " Ndisc[DEL] message for %s skipped. entry_exists %d",
                    show_ipv6_str((char *)ndisc_msg->ipv6_addr), entry_exists);
        }
    }

    return;
}