NUD 网络地址不可达是kernel检测网络状态的一种机制,通过发送特定个数的ARP REQ 给网络,如果网络不回复APR RSP,触发断开连接或者触发roaming.
在android phone上有2种实现方式,一种是在WIFI 驱动中实现,一种是在framework中实现。
Kernel 设计
/net/core/neighbour.c 中,
在neightbl_fill_parms中提供nl80211接口供上层配置ARP UCAST和MCAST的个数,
static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) { NEIGH_VAR(parms, UCAST_PROBES)) || nla_put_u32(skb, NDTPA_MCAST_PROBES, NEIGH_VAR(parms, MCAST_PROBES)) || nla_put_u32(skb, NDTPA_MCAST_REPROBES, NEIGH_VAR(parms, MCAST_REPROBES)) ||
在如下
neigh_alloc时,注册了一个timer,在neigh_timer_handler中
timer_setup(&n->timer, neigh_timer_handler, 0);
如果probe的次数大于预定的ARP 个数,网络还不回复,
if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { WRITE_ONCE(neigh->nud_state, NUD_FAILED); notify = 1; neigh_invalidate(neigh); goto out; }
触发neigh_update_notify,
out: write_unlock(&neigh->lock); } if (notify) neigh_update_notify(neigh, 0);
发送NETEVENT_NEIGH_UPDATE给驱动,同时发送NL消息RTM_NEWNEIGH 给上层。
static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) { call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); }
驱动实现
注册notifier_call的回调函数,当notifier_call收到NETEVENT_NEIGH_UPDATE时,发送消息 nud_state_osif_to_dp给data path模块,
static struct notifier_block wlan_netevent_nb = { .notifier_call = os_if_dp_nud_netevent_cb };
static int os_if_dp_nud_netevent_cb(struct notifier_block *nb, unsigned long event, void *data) { switch (event) { case NETEVENT_NEIGH_UPDATE: ucfg_dp_nud_event((struct qdf_mac_addr *)netdev->dev_addr, (struct qdf_mac_addr *)&neighbor->ha[0], nud_state_osif_to_dp(neighbor->nud_state)); break;
WLAN HDD main 中,调用hdd_register_notifiers注册的回调,
static int hdd_register_notifiers(struct hdd_context *hdd_ctx) { ret = osif_dp_nud_register_netevent_notifier(hdd_ctx->psoc);
hdd_wlan_start_modules时,初始化
hdd_register_notifiers
回到ucfg_dp_nud_event,它也是一个cb,接收本地地址,网关地址,和nud_state,
static void dp_nud_filter_netevent(struct qdf_mac_addr *netdev_addr, struct qdf_mac_addr *gw_mac_addr, uint8_t nud_state)
case DP_NUD_FAILED: dp_info("DP_NUD_FAILED [0x%x]", nud_state); /* * This condition is to handle the scenario where NUD_FAILED * events are received without any NUD_PROBE/INCOMPLETE event * post roaming. Nud state is set to NONE as part of roaming. * NUD_FAILED is not honored when the curr state is any state * other than NUD_PROBE/INCOMPLETE so post roaming, nud state * is moved to DP_NUD_PROBE to honor future NUD_FAILED events. */ if (dp_intf->nud_tracking.curr_state == DP_NUD_NONE) { dp_nud_capture_stats(dp_intf, DP_NUD_PROBE); dp_nud_set_tracking(dp_intf, DP_NUD_PROBE, true); } else { dp_nud_process_failure_event(dp_intf); } break;
发送一个dp_nud_process_failure_event,更新最新的tx_transmitted,acked,gw_rx_pkt,rx_received状态,gateway.
触发nud_event_work */ static void dp_nud_process_failure_event(struct wlan_dp_intf *dp_intf) { uint8_t curr_state; curr_state = dp_intf->nud_tracking.curr_state; if (curr_state == DP_NUD_PROBE || curr_state == DP_NUD_INCOMPLETE) { dp_nud_capture_stats(dp_intf, DP_NUD_FAILED); if (dp_nud_honour_failure(dp_intf)) { dp_intf->nud_tracking.curr_state = DP_NUD_FAILED; qdf_sched_work(0, &dp_intf ->nud_tracking.nud_event_work);
处理dp_nud_failure_work,
dp_ctx->dp_ops.dp_nud_failure_work(dp_ctx->dp_ops.callback_ctx, dp_intf->intf_id);
dp_nud_failure_work=hdd_nud_failure_work
如果注册了data stall event,发送DATA_STALL_LOG_NUD_FAILURE,
如果是roaming offload 功能使能,sta mode,尝试roaming到新的AP
static void __hdd_nud_failure_work(struct hdd_adapter *adapter) { if (soc && ucfg_dp_nud_fail_data_stall_evt_enabled()) { hdd_dp_err("Data stall due to NUD failure"); cdp_post_data_stall_event (soc, DATA_STALL_LOG_INDICATOR_HOST_DRIVER, DATA_STALL_LOG_NUD_FAILURE, OL_TXRX_PDEV_ID, 0XFF, DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); } if (adapter->device_mode == QDF_STA_MODE && ucfg_dp_is_roam_after_nud_enabled(hdd_ctx->psoc)) { hdd_handle_nud_fail_sta(hdd_ctx, adapter); return; } hdd_handle_nud_fail_non_sta(adapter);
同时把目前AP 加入到black list.
android 实现
定义参数
config_nud_steadystate_solicit_num 10
config_nud_steadystate_solicit_interval 750
受到RTM_NEWNEIGH 消息,解析消息内容
case:NetlinkConstants:RTM_NEWNEIGH:
RtNetlinkNeighborMessage.parse(nlmsghdr,bytebuffer)l
handleNeighborLost(event)
调用callback
onReachabilityLost
发送CMD_IP_REACHABILITY_LOST消息,handle handIpReachabilityLost,
调用disconnect
mWifiNative.disconnect(mInterfaceName)
可以看出,driver部分的NUD feature 多了一次roaming,android 部分是直接断开连接。