第6章 IP编址
所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现
本章描述内核中IP地址的表示和管理,包括接口地址结构、地址字节序、地址查找函数等。
6.1 引言
本章讨论的内容
本章讨论的内容:
- 接口地址结构(in_ifaddr)
- 地址的字节序
- 接口地址工具函数
- 多播地址管理
IP编址是网络栈的基础。
6.2 in_ifaddr结构
Internet接口地址结构
每个接口的每个IP地址对应一个in_ifaddr结构:
struct in_ifaddr {
struct ifaddr ia_ifa; /* 通用接口地址结构 */
struct in_ifaddr *ia_next; /* 下一个Internet地址 */
struct in_addr ia_addr; /* IP地址(网络字节序) */
struct in_addr ia_netmask; /* 子网掩码(网络字节序) */
struct in_addr ia_net; /* 网络号(网络字节序) */
struct in_addr ia_subnet; /* 子网号(网络字节序) */
struct in_addr ia_broadaddr; /* 广播地址(网络字节序) */
struct in_addr ia_dstaddr; /* 点对点目的地址 */
/* ... 其他字段 ... */
};结构说明
| 字段 | 说明 |
|---|---|
| ia_ifa | 通用接口地址结构(ifaddr),第一个成员 |
| ia_next | 指向下一个in_ifaddr,所有地址链接成链表 |
| ia_addr | 接口的IP地址 |
| ia_netmask | 子网掩码 |
| ia_net | 网络号(地址 & 掩码) |
| ia_subnet | 子网号 |
| ia_broadaddr | 广播地址 |
| ia_dstaddr | 点对点链路的对端地址 |
第一个成员是ifaddr,可以强制类型转换。
多地址支持
一个接口可以有多个IP地址:
- 所有地址链接在ia_next链表中
- 每个地址有自己的掩码、广播地址等
- 用于虚拟主机、多网络等场景
全局链表:
- 所有in_ifaddr结构链接在一个全局链表中
- 用于全局地址查找
- in_ifaddr链表头
一个接口多个地址,灵活支持各种场景。
6.3 地址的字节序
两种字节序
| 类型 | 说明 | 使用场景 |
|---|---|---|
| 网络字节序 | 大端序(big-endian) | 网络传输、in_addr、sockaddr_in |
| 主机字节序 | 取决于CPU(x86是小端) | 内核变量、计算 |
为什么需要两种字节序:
- 网络字节序:统一网络上的格式,不同CPU可以互通
- 主机字节序:CPU原生格式,计算效率高
网络传输用网络字节序,本地计算用主机字节序。
转换函数
常用的转换函数:
| 函数 | 说明 |
|---|---|
| htonl | 主机到网络(long,32位) |
| htons | 主机到网络(short,16位) |
| ntohl | 网络到主机(long,32位) |
| ntohs | 网络到主机(short,16位) |
使用场景:
- 发送数据前:主机字节序 → 网络字节序
- 接收数据后:网络字节序 → 主机字节序
- 地址比较:统一用主机字节序
地址的存储方式
in_addr结构:
struct in_addr {
u_long s_addr; /* IP地址,网络字节序 */
};- 用于网络传输
- 用于sockaddr_in
- 按网络字节序存储
内核变量:
- 通常用u_long类型
- 按主机字节序存储
- 方便计算和比较
地址按主机字节序以u_long变量存储,方便计算;in_addr和sockaddr_in按网络字节序存储,用于网络传输。
6.4 接口地址工具函数
常用的地址查找函数
| 函数 | 功能 |
|---|---|
| ifa_ifwithaddr | 精确匹配完整地址 |
| ifa_ifwithdstaddr | 匹配点对点目的地址 |
| ifa_ifwithnet | 匹配网络号 |
| ifa_ifwithaf | 匹配地址族 |
| ifaof_ifpforaddr | 找指定接口上最合适的地址 |
| ifa_ifwithroute | 为路由找合适的地址 |
| ifunit | 根据接口名字找接口结构 |
ifa_ifwithaddr
精确匹配地址:
struct ifaddr *
ifa_ifwithaddr(addr)
struct sockaddr *addr;
{
/*
* 遍历所有接口的所有地址
* 精确匹配
*/
}- 完全匹配整个地址
- 用于判断地址是否是本机地址
ifa_ifwithnet
匹配网络号:
struct ifaddr *
ifa_ifwithnet(addr)
struct sockaddr *addr;
{
/*
* 遍历所有接口的所有地址
* 用掩码匹配网络号
* 找最匹配的
*/
}- 用子网掩码匹配
- 找在同一个网络的地址
- 最长匹配优先
ifaof_ifpforaddr
指定接口上找最合适的地址:
struct ifaddr *
ifaof_ifpforaddr(addr, ifp)
struct sockaddr *addr;
struct ifnet *ifp;
{
/*
* 在指定接口的地址中找
* 最匹配目标地址的
*/
}- 在指定接口上找
- 用于选择源地址
查找优先级
地址查找的优先级:
- 精确匹配主机地址
- 匹配子网
- 匹配网络
- 默认地址
从具体到宽泛,找最匹配的。
6.5 多播地址
IP多播地址
D类地址:
- 范围:224.0.0.0 - 239.255.255.255
- 最高四位是1110
- 用于多播通信
特殊多播地址:
- 224.0.0.1:所有主机
- 224.0.0.2:所有路由器
- 224.0.0.0 - 224.0.0.255:链路本地,不转发
多播地址管理
每个接口维护多播地址列表:
- ia_multiaddrs链表
- 每个地址有引用计数
- 多个进程可以加入同一个组
加入组:
- 应用调用setsockopt(IP_ADD_MEMBERSHIP)
- 内核检查是否已在列表中
- 不在则添加,引用计数=1
- 已在则引用计数+1
- 通知网卡加入多播组
离开组:
- 应用调用setsockopt(IP_DROP_MEMBERSHIP)
- 引用计数-1
- 计数为0时,从列表移除
- 通知网卡离开多播组
引用计数,支持多个进程加入同一组。
6.6 小结
in_ifaddr结构
-
结构定义
- ia_ifa:通用接口地址
- ia_next:链表指针
- ia_addr:IP地址
- ia_netmask:子网掩码
- ia_broadaddr:广播地址
-
多地址支持
- 一个接口多个地址
- 全局链表管理
- 灵活支持各种场景
地址字节序
-
两种字节序
- 网络字节序:大端,用于传输
- 主机字节序:CPU原生,用于计算
-
转换函数
- htonl / htons:主机到网络
- ntohl / ntohs:网络到主机
-
存储方式
- in_addr:网络字节序
- 内核变量:主机字节序
地址工具函数
-
查找函数
- ifa_ifwithaddr:精确匹配
- ifa_ifwithnet:网络匹配
- ifaof_ifpforaddr:指定接口查找
- ifunit:按名找接口
-
查找优先级
- 主机地址 > 子网 > 网络 > 默认
- 最长匹配优先
多播地址
-
D类地址
- 224.0.0.0 - 239.255.255.255
- 用于多播通信
-
管理方式
- 每个接口一个链表
- 引用计数
- 支持多进程加入
关键概念
-
in_ifaddr
- 接口IP地址结构
- 包含地址、掩码、广播等
- 一个接口可以有多个
-
字节序
- 网络字节序 vs 主机字节序
- 转换函数
- 不同场景用不同格式
-
地址查找
- 各种查找函数
- 最长匹配优先
- 从具体到宽泛
-
多播地址
- D类地址
- 链表管理
- 引用计数