#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