本文主要是天猫精灵IOT Smart config—— 多播配网代码分析相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 windows11 Ubuntu版本 Ubuntu22.04的64位版本 VMware® Workstation 16 Pro 16.2.3 build-19376536 终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
点击查看本文参考资料
点击查看相关文件下载
一、多播配网代码在哪? 多播配网的相关代码在这里:
1 2 3 4 5 6 7 8 9 hk@vm:~/AliOS-Things-SDK/components/linkkit/wifi_provision$ tree -L 2 . ├── # ...... ├── frameworks ├── # ...... ├── mcast_smartconfig │ └── mcast_smartconfig.c 15 directories, 55 files
为什么文件这么少?其实它有大部分的代码和零配是一样的,比如配网开始、配网初始化等,有一部分都是共用的,其实主要区别是在处理报文的过程,其他的大部分过程都是一样的。
二、多播报文解析 1. 多播报文在哪解析? 1.1 函数调用关系
我们看这个ieee80211_data_extract()函数,其实前面有提到过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 int ieee80211_data_extract (uint8_t *in, int len, int link_type, struct parser_res *res, signed char rssi) { struct ieee80211_hdr *hdr ; int alink_type = ALINK_INVALID; int pkt_type = PKG_INVALID; int i, fc; hdr = (struct ieee80211_hdr *)zconfig_remove_link_header(&in, &len, link_type); if (len <= 0 ) goto drop; fc = hdr->frame_control; for (i = 0 ; i < sizeof (awss_protocol_couple_array) / sizeof (awss_protocol_couple_array[0 ]); i ++) { awss_protocol_process_func_type protocol_func = awss_protocol_couple_array[i].awss_protocol_process_func; if (protocol_func == NULL ) continue ; alink_type = protocol_func((uint8_t *)hdr, len, link_type, res, rssi); if (alink_type != ALINK_INVALID) break ; } if (alink_type == ALINK_INVALID) goto drop; if (alink_type != ALINK_HT_CTRL) { res->src = ieee80211_get_SA(hdr); res->dst = ieee80211_get_DA(hdr); res->bssid = ieee80211_get_BSSID(hdr); res->tods = ieee80211_has_tods(fc); } do { awss_protocol_finish_func_type finish_func = awss_protocol_couple_array[i].awss_protocol_finish_func; if (finish_func) pkt_type = finish_func(res); } while (0 ); drop: return pkt_type; }
可以看到这里主要是调用了两个函数指针指向的函数:
1 2 awss_protocol_couple_array[i].awss_protocol_process_func awss_protocol_couple_array[i].awss_protocol_finish_func
1.2 awss_protocol_couple_array[]数组 这个数组定义在:AOS_SDK_PATH/components/linkkit/wifi_provision/frameworks/ieee80211/zconfig_ieee80211.c中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct awss_protocol_couple_type awss_protocol_couple_array [] = {#ifdef AWSS_SUPPORT_HT40 {ALINK_HT_CTRL, awss_ieee80211_ht_ctrl_process, awss_recv_callback_ht_ctrl}, #endif #ifdef AWSS_SUPPORT_APLIST {ALINK_APLIST, awss_ieee80211_aplist_process, NULL }, #endif #ifdef AWSS_SUPPORT_AHA {ALINK_DEFAULT_SSID, awss_ieee80211_aha_process, awss_recv_callback_aha_ssid}, #endif #ifndef AWSS_DISABLE_ENROLLEE {ALINK_ZERO_CONFIG, awss_ieee80211_zconfig_process, awss_recv_callback_zconfig}, #endif #ifdef AWSS_SUPPORT_SMARTCONFIG_WPS {ALINK_WPS, awss_ieee80211_wps_process, awss_recv_callback_wps}, #endif #ifdef AWSS_SUPPORT_SMARTCONFIG {ALINK_BROADCAST, awss_ieee80211_smartconfig_process, awss_recv_callback_smartconfig}, #endif #ifdef AWSS_SUPPORT_SMARTCONFIG_MCAST {ALINK_BROADCAST, awss_ieee80211_mcast_smartconfig_process, awss_recv_callback_mcast_smartconfig}, #endif #ifdef AWSS_SUPPORT_DISCOVER {ALINK_BROADCAST, aws_discover_callback, NULL }, #endif };
我们这里分析广播配网的代码,所以这里主要是看这两个函数:
1 2 3 #ifdef AWSS_SUPPORT_SMARTCONFIG {ALINK_BROADCAST, awss_ieee80211_smartconfig_process, awss_recv_callback_smartconfig}, #endif
2. awss_ieee80211_mcast_smartconfig_process()函数 awss_ieee80211_mcast_smartconfig_process() 函数定义在AOS_SDK_PATH/components/linkkit/wifi_provision/mcast_smartconfig/mcast_smartconfig.c中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 int awss_ieee80211_mcast_smartconfig_process (uint8_t *ieee80211, int len, int link_type, struct parser_res *res, signed char rssi) { int hdrlen, fc, seq_ctrl; struct ieee80211_hdr *hdr ; uint8_t *data, *bssid_mac, *dst_mac; uint8_t encry = ZC_ENC_TYPE_INVALID; uint8_t tods; if (ieee80211 == NULL || zconfig_finished) { return ALINK_INVALID; } if (awss_get_config_press() == 0 ) { return ALINK_INVALID; } hdr = (struct ieee80211_hdr *)ieee80211; fc = hdr->frame_control; seq_ctrl = hdr->seq_ctrl; if (!ieee80211_is_data_exact(fc)) { return ALINK_INVALID; } if (ieee80211_has_tods(fc) == ieee80211_has_fromds(fc)) { return ALINK_INVALID; } if (ieee80211_has_frags(fc)) { return ALINK_INVALID; } dst_mac = (uint8_t *)ieee80211_get_DA(hdr); if (0x1 != dst_mac[0 ] || 0x0 != dst_mac[1 ] || 0x5e != dst_mac[2 ]) { #ifdef VERBOSE_MCAST_DEBUG awss_debug("error type, %x, %x, %x\n" , dst_mac[0 ], dst_mac[1 ], dst_mac[2 ]); #endif return ALINK_INVALID; } bssid_mac = (uint8_t *)ieee80211_get_BSSID(hdr); hdrlen = ieee80211_hdrlen(fc); if (hdrlen > len) { return ALINK_INVALID; } #ifdef _PLATFORM_QCOM_ hdrlen = (hdrlen + 3 ) & 0xFC ; #endif res->u.br.data_len = len - hdrlen; res->u.br.sn = IEEE80211_SEQ_TO_SN(os_le16toh(seq_ctrl)); data = ieee80211 + hdrlen; tods = ieee80211_has_tods(fc); do { #ifdef AWSS_SUPPORT_APLIST struct ap_info *ap_info ; ap_info = zconfig_get_apinfo(bssid_mac); if (ap_info && ZC_ENC_TYPE_INVALID != ap_info->encry[tods]) { encry = ap_info->encry[tods]; } else #endif { if (!ieee80211_has_protected(fc)) { encry = ZC_ENC_TYPE_NONE; } else { if (len < 8 ) { return ALINK_INVALID; } if (!(ieee80211[3 ] & 0x3F )) { encry = ZC_ENC_TYPE_WEP; } else if (data[3 ] & (1 << 5 )) { if (data[1 ] == ((data[0 ] | 0x20 ) & 0x7F )) { encry = ZC_ENC_TYPE_TKIP; } if (data[2 ] == 0 && (!(data[3 ] & 0x0F ))) { encry = ZC_ENC_TYPE_AES; } } } } } while (0 ); if (encry == ZC_ENC_TYPE_INVALID) { awss_warn("invalid encry type!\r\n" ); } res->u.br.encry_type = encry; res->src = ieee80211_get_SA(hdr); res->dst = ieee80211_get_DA(hdr); res->bssid = ieee80211_get_BSSID(hdr); res->tods = ieee80211_has_tods(fc); return ALINK_BROADCAST; }
可以看出,这个函数主要是对基本信息进行解析,并且判断是否是smart config多播报文。
3. awss_recv_callback_mcast_smartconfig()函数 3.1 序列号sn 下面会有一个sn的概念,他是干嘛用的?我们看一下抓到的包:
seq(Sequence Number)
:32bits
,表示这个数据包的序列号,每个帧的seq都是不一样的。可以根据seq
来确定顺序,并且能够确定是否有数据包丢失。我们在配网的时候,UDP广播报文是很可能会产生丢帧的,接收到的顺序可能不是发送的顺序,这个seq可以帮助设备分辨收到的帧是否是连续的。比如丢的,重复的,都可以被标记出来进行对应的处理。
3.2 函数解析 awss_recv_callback_mcast_smartconfig()函数定义在AOS_SDK_PATH/components/linkkit/wifi_provision/mcast_smartconfig/mcast_smartconfig.c中,这里摆一张有效数据的图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 int awss_recv_callback_mcast_smartconfig (struct parser_res *res) { uint8_t index = *(res->dst + 3 ) & (0x7f ) ; uint8_t payload1 = *(res->dst + 4 ); uint8_t payload2 = *(res->dst + 5 ); uint8_t frame_offset; uint16_t len; uint8_t encry_type; index = index << 1 ; if (1 == get_all) { return FAILURE_RETURN; } if (index > MCAST_MAX_LEN) { #ifdef VERBOSE_MCAST_DEBUG awss_debug("error index\n" ); #endif return FAILURE_RETURN; } len = res->u.br.data_len; encry_type = res->u.br.encry_type; frame_offset = zconfig_fixed_offset[encry_type][0 ]; len -= frame_offset; if (len != 1 ) { #ifdef VERBOSE_MCAST_DEBUG awss_debug("error len, len is %d\n" , len); #endif return FAILURE_RETURN; } if (!memcmp (mcast_src_mac, zero_mac, ETH_ALEN)) { memcpy (mcast_bssid_mac, res->bssid, ETH_ALEN); memcpy (mcast_src_mac, res->src, ETH_ALEN); } else { if (memcmp (mcast_src_mac, res->src, ETH_ALEN)) { return FAILURE_RETURN; } } if (0 == find_channel_from_aplist) { lock_mcast_channel(res, encry_type); } processed_packet++; awss_debug("mcast: index is %d, %d, %d, %d\n" , index, payload1, payload2, len); mcast_smartconfig_data.data[index] = payload1; receive_record[index] = 1 ; index++; mcast_smartconfig_data.data[index] = payload2; receive_record[index] = 1 ; if (0 != receive_record[0 ]) { int remain = -1 ; mcast_smartconfig_data.tlen = mcast_smartconfig_data.data[0 ]; remain = mcast_receive_done(); if (0 == remain) { get_all = 1 ; parse_result(); } } return PKG_MCAST_FRAME; }
3.3 parse_result() 解析SSID和密码等相关数据的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 int parse_result () { int offset = 0 ; int ret, valid_bssid; uint8_t BIT0_passwd = 0 ; uint8_t BIT1_ssid = 0 ; uint8_t BIT2_token = 0 ; uint8_t BIT6_7_version = 0 ; awss_debug("mcast: tlen is %d\n" , mcast_smartconfig_data.tlen); mcast_smartconfig_data.flag = mcast_smartconfig_data.data[1 ]; offset = 2 ; awss_debug("mcast: flag is %d\n" , mcast_smartconfig_data.flag); BIT0_passwd = 0x1 & mcast_smartconfig_data.flag; BIT1_ssid = 0x1 & (mcast_smartconfig_data.flag >> 1 ); BIT2_token = 0x1 & (mcast_smartconfig_data.flag >> 2 ); BIT6_7_version = 0x3 & (mcast_smartconfig_data.flag >> 6 ); if (0x3 != BIT6_7_version) { awss_err("error version" ); return -1 ; } if (1 == BIT0_passwd) { mcast_smartconfig_data.passwd_len = mcast_smartconfig_data.data[2 ]; awss_debug("mcast: passwd_len is %d\n" , mcast_smartconfig_data.passwd_len); mcast_smartconfig_data.passwd = &(mcast_smartconfig_data.data[3 ]); offset = 3 + mcast_smartconfig_data.passwd_len; } if (1 == BIT2_token) { mcast_smartconfig_data.token_len = mcast_smartconfig_data.data[offset]; awss_debug("mcast: token_len is %d\n" , mcast_smartconfig_data.token_len); offset++; mcast_smartconfig_data.token = &(mcast_smartconfig_data.data[offset]); awss_debug("mcast: token is %.*s\n" , mcast_smartconfig_data.token_len, mcast_smartconfig_data.token); offset += mcast_smartconfig_data.token_len; } if (1 == BIT1_ssid) { mcast_smartconfig_data.ssid_len = mcast_smartconfig_data.data[offset]; awss_debug("mcast: ssid_len is %d\n" , mcast_smartconfig_data.ssid_len); offset++; mcast_smartconfig_data.ssid = &(mcast_smartconfig_data.data[offset]); awss_debug("mcast: ssid is %.*s\n" , mcast_smartconfig_data.ssid_len, mcast_smartconfig_data.ssid); offset += mcast_smartconfig_data.ssid_len; } mcast_smartconfig_data.bssid_type_len = mcast_smartconfig_data.data[offset]; awss_debug("mcast: bssid_type_len is %d\n" , mcast_smartconfig_data.bssid_type_len); offset++; mcast_smartconfig_data.bssid = &(mcast_smartconfig_data.data[offset]); offset += mcast_smartconfig_data.bssid_type_len & 0x1f ; mcast_smartconfig_data.checksum = mcast_smartconfig_data.data[offset]; awss_debug("mcast: checksum is %d\n" , mcast_smartconfig_data.checksum); awss_debug("mcast: total processed %d packagets \n" , processed_packet); valid_bssid = set_zc_bssid(); set_zc_ssid(valid_bssid); ret = verify_checksum(); if (SUCCESS_RETURN != ret) { awss_err("mcast: checksum mismatch\n" ); return -1 ; } ret = decode_passwd(); gen16ByteToken(); reset_mcast_data(); if (SUCCESS_RETURN != ret) { awss_err("mcast: passwd error\n" ); return -1 ; } zconfig_set_state(STATE_RCV_DONE, 0 , mcast_locked_channel); return SUCCESS_RETURN; }
3.4 密码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int decode_passwd () { int passwd_len = 0 ; passwd_len = mcast_smartconfig_data.passwd_len; memset (zc_passwd, 0 , ZC_MAX_PASSWD_LEN); aes_decrypt_string((char *)(mcast_smartconfig_data.passwd), (char *)zc_passwd, passwd_len, 1 , awss_get_encrypt_type(), 0 , NULL ); if (is_utf8((const char *)zc_passwd, passwd_len) == 0 ) { awss_trace("passwd err\r\n" ); awss_event_post(IOTX_AWSS_PASSWD_ERR); AWSS_UPDATE_STATIS(AWSS_STATIS_SM_IDX, AWSS_STATIS_TYPE_PASSWD_ERR); return FAILURE_RETURN; } return SUCCESS_RETURN; }