函数原型:int socket(int domain, int type, int protocol);
domain指定地址类型,通常有:
- AF_INET: IPV4地址
- AF_INET6: IPv6地址
- AF_UNIX: UNIX地址(local socket)
- AF_PACKET: 链路层地址(以太网等)
type指定socket的类型,通常的值有:
- SOCK_STREAM
- SOCK_DGRAM
- SOCK_RAW
protocol指定协议,通常有:(填0让内核根据前两个参数自动选择合适的协议)
- IPPROTO_UDP: UDP协议
- IPPROTO_TCP: TCP协议
- IPPROTO_ICMP: ICMP协议
- IPPROTO_RAW: IP协议 (type只能是SOCK_RAW)
每一种协议都有自己特定的类型,比如TCP协议IPPROTO_TCP,它的类型是SOCK_STREAM,UDP协议
IPPROTO_UDP,它的类型是SOCK_DGRAM,ICMP协议IPPROTO_ICMP,它的类型是SOCK_DGRAM
在创建socket时如果类型使用的是协议本身的类型,则在发送数据包的时候只需要发送payload部分
就可以了,协议头由内核添加。
如果类型选择的是SOCK_RAW那么就创建了一个raw socket,在发送数据包的时候需要带上协议头部
ICMP协议有点特殊,不管类型选择的是SOCK_DGRAM还是SOCK_RAW,发送的数据包都要带上协议头。
因为ICMP协议只有负载没有意义,内核也不知道用户期望的ICMP头是什么样的。但是raw ICMP和非raw
ICMP还是有区别的,前者由用户计算校验和,后者由内核计算校验和
raw socket由用户提供协议头,ip头仍然由内核提供。如果用户希望自己提供ip头,则可以设置
socket的IP_HDRINCL选项,但是ip头仍然有一些字段由内核提供,比如校验和、长度等,具体细节可
见 man 7 raw
如果由用户来提供ip头,并且目的地址不是0,则socket的ip地址(bind、sendto、sendmsg提供
的地址)用来路由
IP协议IPPROTO_RAW只能用来发送数据包,不能接收数据包,接收数据包需要使用AF_PACKET。
对于接收数据包,非raw socket收到的是协议的payload,而raw socket收到的数据包总是包含
ip头。
icmp有点不一样,因为icmp报文不一定就有payload,所以即使是非raw socket也会收到icmp头
使用AF_INET或AF_INET6用户只能自行控制网络层(IP或IPV6)头,如果想要控制链路层,自行构建二层包, 需要使用AF_PACKET,这个时候的type可以指定为SOCK_DGRAM或SOCK_RAW。使用SOCK_DGRAM时,用 户填充二层协议的payload,协议头(如以太网头)由内核填充;当使用SOCK_RAW时,二层头由用户填充。 在用户自行处理以太网头时,会在报文里填上目的MAC,但仍然需要调用sendto指定目的MAC来发送数据报文。
man 7 raw for raw socket man 7 packet for packet socket