我们先来回顾一下以太网帧的格式,以太网帧包括48bit的目标地址和源地址,接下来是一个16bit的类型字段,它标识这个帧所携带的数据的格式。对于IP分组,类型是0x0800(2048)。帧的最后是一个32bit的CRC(循环冗余检验),它用来检查帧中的差错。帧的格式如下图所示:
我们用48bit的以太网地址作为硬件地址。IP地址到硬件地址之间的转换用ARP协议。而硬件地址到I P地址的转换用RARP协议。以太网地址有两种类型:单播和多播。一个单播地址描述一个单一的以太网接口,而一个多播地址描述一组以太网接口。一个以太网广播是一个所有接口都接收的多播。
当一个完整的帧可用时,接口就产生一个中断,并且内核调用leintr。leintr检测硬件,并且如果有一个帧到达,就调用leread把这个帧从接口转移到一个mbuf链中。如果硬件报告一个帧已传输完或发现一个差错(如一个有错误的检验和),则leintr更新相应的接口统计,复位这个硬件,并调用lestart来传输另一个帧。所有以太网设备驱动程序将它们接收到的帧传给ether_input做进一步的处理。设备驱动程序构造的mbuf链不包括以太网首部,以太网首部作为一个独立的参数传递给 ether_input。
每个协议有它自己的地址格式。BSD在一个sockaddr结构中处理通用的地址。sa_len指示地址的长度(OSI和Unix域协议有不同长度的地址),sa_family指示地址的类型。成员sa_len和sa_family允许协议无关代码操作来自多个协议的变长的sockaddr结构。剩下的成员sa_data,包含一个协议相关格式的地址。sa_data定义为一个14字节的数组,但当sockaddr结构覆盖更大的内存空间时,sa_data可能会扩展到253字节。sa_len仅有一个字节,因此整个地址,包括sa_len和sa_family必须不超过256字节。
这是C语言的一种通用技术,它允许程序员把一个结构中的最后一个成员看成是可变长的。
每个协议定义一个专用的sockaddr结构,该结构复制成员sa_len和sa_family,但按那个协议的要求来定义成员sa_data。存储在sa_data中的地址是一个传输地址;它包含足够的信息来标识同一主机上的多个通信端点。在第6章我们要查看Internet地址结构sockaddr_in,它包含了一个IP地址和一个端口号。
在以前的读书笔记中,描述了通用的sockaddr和ifaddr结构,很多做过socket编程的人对于这些结构 都比较陌生,因为这个是通过的结构,不仅仅IP协议会用到,其它协议也会用到,下面会讲到IP专用 的结构:sockaddr_in和in_ifaddr。看到sockaddr_in,大家心中应该比较释然了,终于看到熟悉的结构了!
由于历史原因,BSD中以网络字节序将Internet地址存储在一个in_addr结构中。这个结构只有一个成员s_addr,它包含这个地址。注意,一般为了方便本地对地址的处理,地址一般是以主机 字节序存储的,这里是一个例外。
这里讲到字节序问题,我们引申一下,大概的介绍一下字节序的基本概念:
字节序有两种,大端字节序和小端字节序,Intel的X86系列芯片是小端字节序,IBM的PowerPC芯片是大 端字节序,所谓本机字节序,本机使用什么芯片,本机字节序就对应芯片的字节序,网络字节序对应大端 字节序。对于数值0x1234,小端字节序0x12放在高地址,0x34放在低地址,大端字节序0x12放在低地址,0x34放在高地址。
u_long s_addr;
};
struct sockaddr_in {
u_char sin_len;
u_char sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
in_ifaddr是为Internet协议定义的接口地址结构。对于每个指派给一个接口的IP地址,分配了一个in_ifaddr结构,并且添加到接口地址列表中和IP地址全局列表中。
struct ifaddr ia_ifa; /* protocol-independent info */
#define ia_ifp ia_ifa.ifa_ifp
#define ia_flags ia_ifa.ifa_flags
/* ia_{,sub}net{,mask} in host order */
u_long ia_net; /* network number of interface */
u_long ia_netmask; /* mask of net part */
u_long ia_subnet; /* subnet number, including net */
u_long ia_subnetmask; /* mask of subnet part */
struct in_addr ia_netbroadcast; /* to recognize net broadcasts */
struct in_ifaddr *ia_next; /* next in list of internet addresses */
struct sockaddr_in ia_addr; /* reserve space for interface name */
struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
#define ia_broadaddr ia_dstaddr
struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
struct in_multi *ia_multiaddrs; /* list of multicast addresses */
};
struct in_aliasreq {
char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
struct sockaddr_in ifra_addr;
struct sockaddr_in ifra_broadaddr;
#define ifra_dstaddr ifra_broadaddr
struct sockaddr_in ifra_mask;
};
sin_port是一个网络字节序(不是主机字节序)的16bit值,用来分用运输层报文。sin_addr 标识一个32bitInternet地址。