第二十六章 NETBIOS26.1 NETBIOS例程概述 NETBIOS例程,可以为W7500EVB提供设备名称,该设备名称仅在所处网络中使用,设备名称与IP地址功能相同,相较于IP地址或MAC地址而言,设备名称更为灵活,方便记忆。简单而言就是可以通过设备名称来实现对IP地址的访问。 26.2 NETBIOS协议简介NETBIOS协议是由IBM公司开发,主要用于数十台计算机的小型局域网。 NETBIOS 原来是作为THE网络控制器为 IBM 局域网设计的,是通过特定硬件用来和网络操作系统连接的软件层。NETBIOS经扩展,允许程序使用NETBIOS接口来操作IBM令牌环结构。 NETBIOS已被公认为工业标准,通常参照NETBIOS-compatible LANs。 NETBIOS协议是一种在局域网上的程序可以使用的应用程序编程接口(API),为程序提供了请求低级服务的统一的命令集,作用是为了给局域网提供网络以及其他特殊功能,几乎所有的局域网都是在NETBIOS协议的基础上工作的。 NETBIOS最多只能与其他节点建立254个通讯话路,名称最多可有15个字符,通话层应用程序通过它来与远程计算机进行通讯。 26.3 NETBIOS报文格式在Windows操作系统中,默认情况下在安装TCP/IP协议后会自动安装NETBIOS协议。NETBIOS的报文类型较多、结构复杂,在不同的网络环境和不同的用途中会使用不同的报文,可用端口进行区分, WINS协议使用的NETBIOS名字报文使用UDP 137端口,NETBIOS数据报报文使用UDP 138端口,NETBIOS会话报文使用TCP 139端口。NETBIOS数据报报文的总体格式如表26.3.1: 表26.3.1 NETBIOS数据报报文 其中,如果消息类型字段内容为0x10、0x11、0x12时,分别说明NETBIOS的数据报为发送给相邻的特定主机、发送给直连网段内的全部主机还是广播给全部主机的数据报,此时的报文格式如表26.3.2所示: 表26.3.2 NETBIOS数据报报文 表26.3.3 NETBIOS会话报文 NBT中的网络设备会采用哪一种名称解析方法来查找其他设备的IP地址,这要依这台 网络设备所采用的NETBIOS节点类型而定: b-node(使用广播方式) p-node(P2P,对等式,直接询问) m-node(混合方式,先广播,后询问) h-node(混合交互式(P←→M),先询问,再广播) 大部分NBT客户机都是b节点。NBT的b节点的工作方式是:客户机发送一条广播消息到其所在的子网上,这条消息包含要查找的网络设备的IP地址和其本身的MAC地址。该子网的所有设备都会收到这个广播包。匹配该地址的设备如果存在就会根据包含在此数据包中发出该消息的网络的MAC地址做出回应。这个这两个设备此后就可以使用相应地址直接通信了。 26.4 NETBIOS名字服务器的工作原理 由于b-node广播会在网络上产生大量的信息流,尤其是在网络是由多个子网构成的时候,而使用路由器本来就 是要隔离广播信息,可是为了进行名字解析,就不得不转发b-node广播信息包,这就达不到缩减无用网络流量的目的。 使用名字服务器进行解析就能避免这个问题,客户通过对名字服务器进行查询而非广播,信息流就不必传播到各个子 网上,就能减少广播数据,减轻网络的负担节省带宽,并且能有效的提高名字解析的速度及准确性。 实际存在的Windows网络甚至很少利用名字服务器进行名字解析,这就使得这些网络名字解析存在很大问题, 常常会出现不同计算机的网络邻居列表不同,根本原因就是广播方式是没有保证的,必须转向名字服务器方式才能解决名字解析问题。 普通NETBIOS计算机和NBNS服务器进行通信有四个不同的通信过程: l 名字注册:每台NETBIOS计算机启动时,都在名字服务器上注册。这样就保持了数据库的自动更新,并具备动 态更新的特性。名字服务器将返回确认信息,以及这个名字的生存期TTL。如果客户要求的名字已经被占用了,服务器就查 询占用这个名字的客户是否还在网络上,以判断这个名字是否可以再次被使用。这种情况主要发生在Windows计算机死 机后重新登记的过程中,因为此时在计算机死机之前,它在名字服务器中登记的名字还存在,如果名字服务器简单的拒绝提供 名字,那么这个计算机就无法再次获得自己的名字。只有在真正发生冲突的情况下,客户的名字注册才会失败。 l 名字更新:由于每个名字都存在一个生存期TTL,那么当经历了这个TTL一半的时间,客户会向服务器进行更新请求,刷新服务器上的TTL设置。 l 名字释放:客户停机时会与服务器通信释放其占用的NETBIOS名字,其名字TTL超时也会使得服务器释放这个名字。 l 名字识别:客户可以向NBNS服务器发送查询名字的请求,进行名字解析。 有些情况下,客户没有设置支持名字服务器,或者使用的客户软件还不支持名字服务器进行解析,可以通过设置一个WINS代理,由它来在广播数据和查询名字服务器之间进行转换,它可以帮助客户注册并回应客户的广播查询。 NETBIOS名字的解析方法(绑定协议为TCP/IP): 1、NETBIOS 名字缓存——本地的名字缓存通过预先加载或地址解析而将地址在存放在客户机的内存中。其优点是从缓存中查询名字的速度特别快,缺点是缓存中每条记录的生存周期只有10分钟,需要不停的刷新。 2、广播查询——如果名字在缓存中不能被查询到,同时系统没有配置WINS服务器,系统就使用UDP 端口137和138的NETBIOSDatagramDistribUtionSer vice(NDDS,中文意思就是 NETBIOS 数据报分布服务)来进行查询。本地网上的计算机在网络中搜索它们的名字列表,如果目标主机在同一网络中,就发出一个响应。缺点是NDDS服务不能通过路由器,只能在单段网络上使用广播查询。 3、WINS——这个我们已经做了专门的介绍,客户机通过配置使用WINS服务器来登记和撤消自己的名字。其优点是可以指引UDP端口137和138的消息通过路由器,使得可以查询本地网以外的主机。 4、LMHOSTS——这与我们讲到的名字缓存有关了。LMHOSTS 是一种在存储在本地机器上的纯文本的主机文件,采用的是LANManager格式。其一般是在广播查询失败后才用到,其方法是通过标记#PRE将LMHOSTS名预先加载到本地的NetBIO名字缓存,以避免使用广播查询用到的主机名。 26.5 NETBIOS例程解析 NETBIOS例程初始化与DHCP例程相同,使用DHCP获取IP地址。具体内容不再讲述,详解应用函数do_netbios(); [mw_shl_code=applescript,true]1. #define NETBIOS_W7500_NAME "W7500"
2. void do_netbios(void)
3. {
4. unsigned char state;
5. unsigned int len;
6. state = getSn_SR(SOCK_NETBIOS);
7. switch(state)
8. {
9. case SOCK_UDP:
10. if((len=getSn_RX_RSR(SOCK_NETBIOS))>0)
11. {
12. unsigned char rem_ip_addr[4];
13. uint16_t rem_udp_port;
14. char netbios_name[NETBIOS_NAME_LEN+1];
15. NETBIOS_HDR* netbios_hdr;
16. NETBIOS_NAME_HDR* netbios_name_hdr;
17. len=recvfrom(SOCK_NETBIOS,(unsigned
18. char*)&netbios_rx_buf,len,rem_ip_addr,&rem_udp_port);
19. printf("rem_ip_addr=%d.%d.%d.%d:%d\r\n",rem_ip_addr[0],rem_ip_addr[1],rem_ip_addr[2],rem_ip_addr[3],rem_udp_port);
20. netbios_hdr = (NETBIOS_HDR*)netbios_rx_buf;
21. netbios_name_hdr = (NETBIOS_NAME_HDR*)(netbios_hdr+1);
22. /* if the packet is a NetBIOS name query question */
23. if (((netbios_hdr->flags & swaps(NETB_HFLAG_OPCODE)) == swaps(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
24. ((netbios_hdr->flags & swaps(NETB_HFLAG_RESPONSE)) == 0) &&
25. (netbios_hdr->questions == swaps(1)))
26. {
27. printf("netbios name query question\r\n");
28. /* decode the NetBIOS name */
29. netbios_name_decoding( (char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
30. printf("name is %s\r\n",netbios_name);
31. /* if the packet is for us */
32. if (strcmp(netbios_name, NETBIOS_W7500_NAME) == 0)
33. {
34. uint8_t ip_addr[4];
35. NETBIOS_RESP *resp = (NETBIOS_RESP*)netbios_tx_buf;
36. /* prepare NetBIOS header response */
37. resp->resp_hdr.trans_id = netbios_hdr->trans_id;
38. resp->resp_hdr.flags = swaps(NETB_HFLAG_RESPONSE |
39. NETB_HFLAG_OPCODE_NAME_QUERY |
40. NETB_HFLAG_AUTHORATIVE |
41. NETB_HFLAG_RECURS_DESIRED);
42. resp->resp_hdr.questions = 0;
43. resp->resp_hdr.answerRRs = swaps(1);
44. resp->resp_hdr.authorityRRs = 0;
45. resp->resp_hdr.additionalRRs = 0;
46. /* prepare NetBIOS header datas */
47. memcpy( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
48. resp->resp_name.nametype = netbios_name_hdr->nametype;
49. resp->resp_name.type = netbios_name_hdr->type;
50. resp->resp_name.cls = netbios_name_hdr->cls;
51. resp->resp_name.ttl = swapl(NETBIOS_NAME_TTL);
52. resp->resp_name.datalen = swaps(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
53. resp->resp_name.flags = swaps(NETB_NFLAG_NODETYPE_BNODE);
54. getSIPR(ip_addr);
55. memcpy(resp->resp_name.addr,ip_addr,4);
56. //ip_addr_copy(resp->resp_name.addr, netif_default->ip_addr);
57.
58. /* send the NetBIOS response */
59. sendto(SOCK_NETBIOS, (unsigned char*)resp, sizeof(NETBIOS_RESP), rem_ip_addr, rem_udp_port);
60. printf("send response\r\n");
61. }
62. }
63. }
64. break;
65. case SOCK_CLOSED: //socket关闭状态
66. close(SOCK_NETBIOS);
67. socket(SOCK_NETBIOS,Sn_MR_UDP,NETBIOS_PORT,0); //初始化socket信息
68. break;
69. default:
70. break;
71. }
72. }[/mw_shl_code]
首行的宏定义为使用NETBIOS协议例程时,赋予W7500EVB在所处网络中的设备名称,可以根据需求自行更改。 第65行,socket初始状态为关闭,对UDP socket进行初始化操作,如果初始化成功,该socket就会处于UDP模式下,该模式下例程函数对接收的数据报文进行判断解析。23行判断是否有数据报文,如果有了就查询数据报文。29行开始解析接收到的数据报文。32行判断是否是对本网络设备的设备响应,如果是就准备BETBIOS报文头响应。47行开始准备NETBIOS报文头响应信息,59行将准备好的报文头信息发送到服务器。 至此,NETBIOS例程解析结束,编译后烧录打印串口结果如图26.5.1。 图26.5.1 NETBIOS例程打印结果 PC机PING W7500结果如图26.5.2,通过PING “W7500”,成功实现对W7500EVBIP地址的访问。
图26.5.2 PING W7500结果
|