#include <sys/socket.h>
#include <net/netinet.h>
tcp_socket = socket(PF_INET, SOCK_STREAM, 0);
raw_socket = socket(PF_INET, SOCK_RAW, protocol);
udp_socket = socket(PF_INET, SOCK_DGRAM,
protocol);
Linux 实现描述于 RFC791 和
RFC1122 中的 Internet 协议,版本4.
ip 包括遵循 RFC1112
的第二层的多信道广播技术的实现.它也包括含包过滤器的IP路由器.
程序员的接口与 BSD
的套接字(socket)兼容.
要获得关于套接字的更多信息,参见
socket(7)
创建一个IP套接字是通过以
socket(PF_INET, socket_type, protocol)
方式调用 socket(2)
函数来实现的.
有效的套接字类型(socket_type)有:
SOCK_STREAM 用来打开一个
tcp(7) 套接字, SOCK_DGRAM
用来打开一个 udp(7)
套接字,或者是 SOCK_RAW
用来打开一个 raw(7)
套接字用来直接访问 IP
协议. protocol
指的是要接收或者发送出去的包含在
IP 头标识(header)中的 IP
协议.
对于TCP套接字而言,唯一的有效
protocol 值是 0 和 IPPROTO_TCP
对于UDP套接字而言,唯一的有效
protocol 值是 0 和 IPPROTO_UDP.
而对于 SOCK_RAW
你可以指定一个在 RFC1700
中定义的有效 IANA IP
协议代码来赋值.
当一个进程希望接受新的来访包或者连接时,它应该使用
bind(2)
绑定一个套接字到一个本地接口地址.
任意给定的本地(地址,端口)对只能绑定一个IP套接字.
当调用 bind 时中声明了
INADDR_ANY
时,套接字将会绑定到
所有
本地接口.当在未绑定的套接字上调用
listen(2) 或者 connect(2)
时,套接字会自动绑定到一个本地地址设置为
INADDR_ANY
的随机的空闲端口上.
除非你设置了
S0_REUSEADDR
标识,否则一个已绑定的
TCP
本地套接字地址在关闭后的一段时间内不可用.
使用该标识的时候要小心,因为它会使
TCP 变得不可靠.
一个 IP
套接字地址定义为一个
IP
接口地址和一个端口号的组合.
基本 IP
协议不会提供端口号,它们通过更高层次的协议如
udp(7) 和 tcp(7) 来实现.
对于raw套接字, sin_port
设置为IP协议.
struct sockaddr_in {
sa_family_t sin_family; /* 地址族: AF_INET */
u_int16_t sin_port; /* 按网络字节次序的端口 */
struct in_addr sin_addr; /* internet地址 */
};
/* Internet地址. */
struct in_addr {
u_int32_t s_addr; /* 按网络字节次序的地址 */
};
sin_family 总是设置为
AF_INET. 这是必需的;在
Linux 2.2
中,如果该设置缺失,大多数联网函数会返回
EINVAL sin_port
包含按网络字节排序的端口号.端口号在1024以下的称为
保留端口.
只有那些有效用户标识为
0 或者 CAP_NET_BIND_SERVICE
有功能的进程才可以
bind(2)
到这些套接字.注意原始的(raw)IPv4协议没有这样的端口概念,它们只通过更高的协议如
tcp(7) 和 udp(7) 来实现.
sin_addr 指的是 IP
主机地址. 在 struct in_addr
中的 addr
部分包含按网络字节序的主机接口地址.
in_addr
应该只能通过使用
inet_aton(3), inet_addr(3), inet_makeaddr(3)
库函数或者直接通过名字解析器(参见
gethostbyname(3)) 来访问. IPv4
地址分成单点广播,广播传送和多点广播地址.
单点广播地址指定了一台主机的单一接口,广播地址指
定了在一个网段上的所有主机,
而多点广播地址则在一个多点传送组中寻址所有主机.
只有当设置了套接字标识
SO_BROADCAST 时,
才能收发数据报到广播地址.
在当前的实现中,面向连接的套接字只允许使用单点传送地址.
注意地址和端口总是按照网络字节序存储的.
这意味着你需要对分配给端口的号码调用
htons(3).
所有在标准库中的地址/端口处理函数都是按网络字节序运行的.
有几个特殊的地址:
INADDR_LOOPBACK (127.0.0.1)
总是代表经由回环设备的本地主机;
INADDR_ANY (0.0.0.0)
表示任何可绑定的地址;
INADDR_BROADCAST (255.255.255.255)
表示任何主机,由于历史的原因,这与绑定为
INADDR_ANY 有同样的效果.
IP
支持一些与协议相关的套接字选项,这些选项可以通过
setsockopt(2)
设置,并可以通过
getsockopt(2) 读取. IP
的套接字选项级别为
SOL_IP
这是一个布尔整型标识,当值为0时为假,否则则为真.
- IP_OPTIONS
- 设置或者获取将由该套接字发送的每个包的
IP 选项.
该参数是一个指向包含选项和选项长度的存储缓冲区的指针.
setsockopt(2)
系统调用设置与一个套接字相关联的
IP 选项. IPv4
的最大选项长度为 40
字节. 参阅 RFC791
获取可用的选项.
如果一个 SOCK_STREAM
套接字收到的初始连接请求包包含
IP 选项时, IP
选项自动设置为来自初始包的选项,同时反转路由头.
在连接建立以后将不允许来访的包修改选项.
缺省情况下是关闭对所有来访包的源路由选项的,你可以用
accept_source_route sysctl
来激活.仍然处理其它选项如时间戳(timestamp).
对于数据报套接字而言,IP
选项只能由本地用户设置.调用带
IP_OPTIONS 的 getsockopt(2)
会把当前用于发送的
IP
选项放到你提供的缓冲区中.
- IP_PKTINFO
- 传递一条包含 pktinfo
结构(该结构提供一些来访包的相关信息)的
IP_PKTINFO 辅助信息.
这个选项只对数据报类的套接字有效.
struct in_pktinfo
{
unsigned int ipi_ifindex; /* 接口索引 */
struct in_addr ipi_spec_dst; /* 路由目的地址 */
struct in_addr ipi_addr; /* 头标识目的地址 */
};
- ipi_ifindex
指的是接收包的接口的唯一索引.
ipi_spec_dst
指的是路由表记录中的目的地址,而
ipi_addr
指的是包头中的目的地址.
如果给 sendmsg (2)传递了
IP_PKTINFO,
那么外发的包会通过在
ipi_ifindex 中指定的接口
发送出去,同时把
ipi_spec_dst
设置为目的地址.
- IP_RECVTOS
- 如果打开了这个选项,则
IP_TOS ,
辅助信息会与来访包一起传递.
它包含一个字节用来指定包头中的服务/优先级字段的类型.
该字节为一个布尔整型标识.
- IP_RECVTTL
- 当设置了该标识时,
传送一条带有用一个字节表示的接收包生存时间(time
to live)字段的 IP_RECVTTL
控制信息.
此选项还不支持
SOCK_STREAM 套接字.
- IP_RECVOPTS
- 用一条 IP_OPTIONS
控制信息传递所有来访的
IP 选项给用户.
路由头标识和其它选项已经为本地主机填好.
此选项还不支持
SOCK_STREAM 套接字.
- IP_RETOPTS
- 等同于 IP_RECVOPTS
但是返回的是带有时间戳的未处理的原始选项和在这段路由中未填入的路由记录项目.
- IP_TOS
- 设置或者接收源于该套接字的每个IP包的
Type-Of-Service (TOS
服务类型)字段.它被用来在网络上区分包的优先级.
TOS
是单字节的字段.定义了一些的标准
TOS 标识: IPTOS_LOWDELAY
用来为交互式通信最小化延迟时间,
IPTOS_THROUGHPUT
用来优化吞吐量,
IPTOS_RELIABILITY
用来作可靠性优化,
IPTOS_MINCOST
应该被用作"填充数据",对于这些数据,低速传输是无关紧要的.
至多只能声明这些 TOS
值中的一个.其它的都是无效的,应当被清除.
缺省时,Linux首先发送
IPTOS_LOWDELAY 数据报,
但是确切的做法要看配置的排队规则而定.
一些高优先级的层次可能会要求一个有效的用户标识
0 或者 CAP_NET_ADMIN 能力.
优先级也可以以于协议无关的方式通过(
SOL_SOCKET, SO_PRIORITY
)套接字选项(参看
socket(7) )来设置.
- IP_TTL
- 设置或者检索从此套接字发出的包的当前生存时间字段.
- IP_HDRINCL
- 如果打开的话,
那么用户可在用户数据前面提供一个
ip 头. 这只对 SOCK_RAW
有效.参看 raw(7)
以获得更多信息.当激活了该标识之后,其值由
IP_OPTIONS 设定,并且 IP_TOS
被忽略.
- IP_RECVERR
- 允许传递扩展的可靠的错误信息.
如果在数据报上激活了该标识,
那么所有产生的错误会在每套接字一个的错误队列中排队等待.
当用户从套接字操作中收到错误时,就可以通过调用设置了
MSG_ERRQUEUE 标识的 recvmsg(2)
来接收. 描述错误的
sock_extended_err
结构将通过一条类型为
IP_RECVERR , 级别为
SOL_IP的辅助信息进行传递.
这个选项对在未连接的套接字上可靠地处理错误很有用.
错误队列的已收到的数据部分包含错误包.
- IP
按照下面的方法使用
sock_extended_err 结构: ICMP
包接收的错误 ee_origin
设为 SO_EE_ORIGIN_ICMP ,
对于本地产生的错误则设为
SO_EE_ORIGIN_LOCAL . ee_type 和 ee_code
设置为 ICMP
头标识的类型和代码字段.
ee_info 包含用于 EMSGSIZE
时找到的 MTU. ee_data
目前没有使用.
当错误来自于网络时,该套接字上所有IP选项都被激活
(IP_OPTIONS, IP_TTL,
等.)并且当做控制信息包含错误包中传递.引发错误的包的有效载荷会以正常数据返回.
- 在 SOCK_STREAM 套接字上,
IP_RECVERR
会有细微的语义不同.它并不保存下次超时的错误,而是立即传递所有进来的错误给用户.
这对 TCP
连接时间很短的情况很有用,因为它要求快速的错误处理.
使用该选项要小心:因为不允许从路由转移和其它正
常条件下正确地进行恢复,它使得TCP变得不可靠,并且破坏协议的规范.
注意TCP没有错误队列;
MSG_ERRQUEUE 对于 SOCK_STREAM
套接字是非法的.
因此所有错误都会由套接字函数返回,或者只返回
SO_ERROR .
- 对于原始(raw)套接字而言,
IP_RECVERR
允许传递所有接收到的ICMP错误给应用程序,否则错误只在连接的套接字上报告出来.
- 它设置或者检索一个整型布尔标识.
IP_RECVERR
缺省设置为off(关闭).
- IP_PMTU_DISCOVER
- 为套接字设置或接收Path
MTU Discovery
setting(路径MTU发现设置).
当允许时,Linux会在该套接字上执行定
义于RFC1191中的Path MTU
Discovery(路径MTU发现). don't
段标识会设置在所有外发的数据报上.
系统级别的缺省值是这样的:
SOCK_STREAM 套接字由 ip_no_pmtu_disc
sysctl
控制,而对其它所有的套接字都被都屏蔽掉了,对于非
SOCK_STREAM 套接字而言,
用户有责任按照MTU的大小对数据分块并在必要的情况下进行中继重发.如果设置了该标识
(用 EMSGSIZE
),内核会拒绝比已知路径MTU更大的包.
Path MTU
discovery(路径MTU发现)标识 |
含义 |
IP_PMTUDISC_WANT |
对每条路径进行设置. |
IP_PMTUDISC_DONT |
从不作Path MTU
Discovery(路径MTU发现). |
IP_PMTUDISC_DO |
总作Path MTU
Discovery(路径MTU发现). |
当允许 PMTU
(路径MTU)搜索时,
内核会自动记录每个目的主机的path
MTU(路径MTU).当它使用
connect(2)
连接到一个指定的对端机器时,可以方便地使用
IP_MTU
套接字选项检索当前已知的
path
MTU(路径MTU)(比如,在发生了一个
EMSGSIZE
错误后).它可能随着时间的推移而改变.
对于带有许多目的端的非连接的套接字,一个特定目的端的新到来的
MTU
也可以使用错误队列(参看
IP_RECVERR) 来存取访问.
新的错误会为每次到来的
MTU 的更新排队等待.
当进行 MTU
搜索时,来自数据报套接字的初始包可能会被丢弃.
使用 UDP
的应用程序应该知道这个并且考虑
其包的中继传送策略.
为了在未连接的套接字上引导路径
MTU 发现进程,
我们可以用一个大的数据报(头尺寸超过64K字节)启动,
并令其通过更新路径
MTU 逐步收缩.
为了获得路径MTU连接的初始估计,可通过使用
connect(2)
把一个数据报套接字连接到目的地址,并通过调用带
IP_MTU选项的 getsockopt(2)
检索该MTU.
- IP_MTU
- 检索当前套接字的当前已知路径MTU.只有在套接字被连接时才是有效的.返回一个整数.只有作为一个
getsockopt(2) 才有效.
- IP_ROUTER_ALERT
- 给该套接字所有将要转发的包设置IP路由器警告(IP
RouterAlert option)选项.
只对原始套接字(raw
socket)有效,这对用户空间的
RSVP后
台守护程序之类很有用.
分解的包不能被内核转发,用户有责任转发它们.套接字绑定被忽略,
这些包只按协议过滤.
要求获得一个整型标识.
- IP_MULTICAST_TTL
- 设置或者读取该套接字的外发多点广播包的生存时间值.
这对于多点广播包设置可能的最小TTL很重要.
缺省值为1,这意味着多点广播包不会超出本地网段,
除非用户程序明确地要求这么做.参数是一个整数.
- IP_MULTICAST_LOOP
- 设置或读取一个布尔整型参数以决定发送的多点广播包是否应该被回送到本地套接字.
- IP_ADD_MEMBERSHIP
- 加入一个多点广播组.参数为
struct ip_mreqn 结构.
struct ip_mreqn
{
struct in_addr imr_multiaddr; /* IP多点传送组地址 */
struct in_addr imr_address; /* 本地接口的IP地址 */
int imr_ifindex; /* 接口索引 */
};
- imr_multiaddr
包含应用程序希望加入或者退出的多点广播组的地址.
它必须是一个有效的多点广播地址.
imr_address
指的是系统用来加入多点广播组的本地接口地址;如果它与
INADDR_ANY
一致,那么由系统选择一个合适的接口.
imr_ifindex
指的是要加入/脱离
imr_multiaddr
组的接口索引,或者设为0表示任何接口.
- 由于兼容性的缘故,老的
ip_mreq
接口仍然被支持.它与
ip_mreqn
只有一个地方不同,就是没有包括
imr_ifindex
字段.这只在作为一个
setsockopt(2) 时才有效.
- IP_DROP_MEMBERSHIP
- 脱离一个多点广播组.参数为
ip_mreqn 或者 ip_mreq
结构,这与 IP_ADD_MEMBERSHIP
类似. IP_MULTICAST_IF
为多点广播套接字设置本地设备.参数为
ip_mreqn 或者 ip_mreq
结构,它与 IP_ADD_MEMBERSHIP
类似.
- 当传递一个无效的套接字选项时,返回
ENOPROTOOPT .
IP协议支持 sysctl
接口配置一些全局选项.sysctl可通过读取或者写入
/proc/sys/net/ipv4/* 文件或使用
sysctl(2)
接口来存取访问.
- ip_default_ttl
- 设置外发包的缺省生存时间值.此值可以对每个套接字通过
IP_TTL 选项来修改.
- ip_forward
- 以一个布尔标识来激活IP转发功能.IP转发也可以按接口来设置
- ip_dynaddr
- 打开接口地址改变时动态套接字地址和伪装记录的重写.
这对具有变化的IP地址的拨号接口很有
用.0表示不重写,1打开其功能,而2则激活冗余模式.
- ip_autoconfig
- 无文档
- ip_local_port_range
- 包含两个整数,定义了缺省分配给套接字的本地端口范围.
分配起始于第一个数而终止于第二个数.
注意这些端口不能与伪装所使用的端口相冲突(尽管这种情况也可以处理).
同时,随意的选择可能会导致一些防火墙包过滤器的问题,它们会误认为本地端口在使用.
第一个数必须至少>1024,最好是>4096以避免与众所周知的端口发生冲突,
从而最大可能的减少防火墙问题.
- ip_no_pmtu_disc
- 如果打开了,缺省情况下不对TCP套接字执行路径MTU发现.
如果在路径上误配置了防火墙(用来丢弃所有
ICMP包)或者误配置了接口
(例如,设置了一个两端MTU不同的端对端连接),路径MTU发现可能会失败.
宁愿修复路径上的损坏的路由器,也好过整个地关闭路径MTU发现,
因为这样做会导致网络上的高开销.
- ipfrag_high_thresh,
ipfrag_low_thresh
- 如果排队等待的IP碎片的数目达到
ipfrag_high_thresh ,
队列被排空为 ipfrag_low_thresh
.
这包含一个表示字节数的整数.
- ip_always_defrag
- [kernel
2.2.13中的新功能;在早期内核版本中,该功能在编译时通过
CONFIG_IP_ALWAYS_DEFRAG 选项来控制]
当该布尔标识被激活(不等于0)时,
来访的碎片(IP包的一部分,这生成于当一些在源端和目的端之间的主机认
定包太大而分割成许多碎片的情况下)将在处理之前重新组合(碎片整理),
即使它们马上要被转发也如此.
只在运行着一台与网络单一连接的防火墙或者透明代理服务器时才这么干;
对于正常的路由器或者主机,
永远不要打开它.
否则当碎片在不同连接中通过时碎片的通信可能会被扰乱.
而且碎片重组也需要花费大量的内存和
CPU 时间.
这在配置了伪装或者透明代理的情况下自动打开.
- neigh/*
- 参看 arp(7)
使用 SO_BROADCAST
选项要小心 - 它在 Linux
中没有权限要求.
不小心的广播很容易导致网络过载.对于新的应用协议而言,最
好是使用多点广播组来替代广播.我们不鼓励使用广播.
有些其它的BSD套接字实现提供了
IP_RCVDSTADDR 和 IP_RECVIF
套接字选项来获得目的地址以及接收数据报的接口.Linux有更通用的
IP_PKTINFO
来完成相同任务.
ENOBUFS,EPERM对EACCES等.)
- ENOTCONN
- 操作只定义于连接的套接字,而该套接字却没有连接.
- EINVAL
- 传递无效的参数.
对于发送操作,这可以因发送到一个
blackhole(黑洞)
路由而引发.
- EMSGSIZE
- 数据报大于该路径上的
MTU,并且它不能被分成碎片.
- EACCES
- 没有必要权限的用户试图执行一项需要某些权限的操作.
这包括: 在没有 SO_BROADCAST
标识设置的情况下发送一个包到广播地址.
通过一条 禁止的
路由发送包. 在没有
CAP_NET_ADMIN
或者有效用户标识不为0的情况下修改防火墙设置.
在没有 CAP_NET_BIND_SERVICE
能力或者有效用户标识不为零0的情况下绑定一个保留端口.
- EADDRINUSE
- 试图绑定到一个已在使用的地址.
- ENOMEM 和
ENOBUFS
- 没有足够的内存可用.
- ENOPROTOOPT
和 EOPNOTSUPP
- 传递无效的套接字选项.
- EPERM
- 用户没有权限设置高优先级,修改配置或者发送信号到请求的进程或组.
- EADDRNOTAVAIL
- 请求一个不存在的接口或者请求的源端地址不是本地的.
- EAGAIN
- 在一个非阻塞的套接字上进行操作会阻塞.
- ESOCKTNOSUPPORT
- 套接字未配置或者请求了一个未知类型的套接字.
- EISCONN
- 在一个已经连接的套接字上调用
connect(2).
- EALREADY
- 在一个非阻塞的套接字上的连接操作已经在进行中.
- ECONNABORTED
- 在一次 accept(2)
执行中连接被关闭.
- EPIPE
- 连接意外关闭或者被对端关闭.
- ENOENT
- 在没有报到达的套接字上调用
SIOCGSTAMP .
- EHOSTUNREACH
- 没有有效路由表记录匹配目的地址.该错误可以被来自远程路由器的
ICMP消息或者因为本地路由表的缘故而引发.
- ENODEV
- 网络设备不可用或者不适于发送IP.
- ENOPKG
- 内核子系统没有配置.
- ENOBUFS,
ENOMEM
- 没有足够的空闲内存.
这常常意味着内存分配因套接字缓冲区的限制而受限,
而不是因为系统内存的缘故,但是这也不是100%正确.
其它错误可能由重叠协议族生成;参看
tcp(7), raw(7), udp(7) 和 socket(7).
IP_PKTINFO, IP_MTU, IP_PMTU_DISCOVER,
IP_PKTINFO, IP_RECVERR 和 IP_ROUTER_ALERT
是Linux 2.2中的新选项.
struct ip_mreqn
也是新出现在Linux
2.2中的.Linux 2.0只支持 ip_mreq.
sysctl是在Linux
2.2中引入的.
为了与Linux
2.0相容,仍然支持用过时的
socket(PF_INET, SOCK_RAW, protocol)
语法打开一个 packet(7)
套接字.我们不赞成这么用,而且应该被
socket(PF_PACKET, SOCK_RAW, protocol)
所代替.主要的区别就是
新的针对一般链接层信息的
sockaddr_ll
地址结构替换了旧的
sockaddr_pkt 地址结构.
有许多不连贯的错误码.
没有描述用来配置特定IP接口选项和ARP表的ioctl.
riser <boomer@ccidnet.com>
《中国linux论坛man手册页翻译计划》:
http://cmpp.linuxforum.net
跋
本页面中文版由中文
man 手册页计划提供。
中文 man
手册页计划:https://github.com/man-pages-zh/manpages-zh