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;
}