Internet 网络协议族
1、linux目前支持多种协议族,每个协议族用一个net_porto_family结构实例来表示,在初始化时,会调用sock_register()函数初始化注册到net_families[NPROTO]中去;

同时出现了一个地址族的概念,目前协议族和地址族是一 一 对应关系。历史上曾经有一个协议族支持多个地址族,实际上从未实现过。在socket.h文件中PF_XX和AF_XX 值一样
2、由于不同协议族的结构差别很大,为了封装统一,以便在初始化时,可以统一接口,于是就有了net_proto_family。其用sock_register统一注册,初始化钩子,具体初始化,其实现见钩子实现,类似于VFS 的实现方式。一种很好的设计思想。
/*ops->create在应用程序创建套接字的时候,引起系统调用,从而在函数__sock_create中执行ops->create netlink为netlink_family_ops
应用层创建套接字的时候,内核系统调用sock_create,然后执行该函数
pf_inet的net_families[]为inet_family_ops,对应的套接口层ops参考inetsw_array中的inet_stream_ops inet_dgram_ops inet_sockraw_ops,
传输层操作集分别为tcp_prot udp_prot raw_prot
netlink的net_families[]netlink_family_ops,对应的套接口层ops为netlink_ops
family协议族通过sock_register注册 传输层接口tcp_prot udp_prot netlink_prot等通过proto_register注册
IP层接口通过inet_add_protocol(&icmp_protocol等注册 ,这些组成过程参考inet_init函数*/
struct net_proto_family {//操作集参考inetsw_array
int family;
int (*create)(struct net *net, struct socket *sock,
int protocol, int kern);协议族的套接字创建函数指针,每个协议族实现都不同
struct module *owner;
};

Internet 协议族的net_proto_family结构实例为inet_family_ops,创建套接字socket时,其调用接口为inet_create().
2、inet_protosw 结构
/* This is used to register socket interfaces for IP protocols. */
struct inet_protosw {
struct list_head list;/* 初始化时将相同的type的inet_protosw散列在同一个链表*/ /* These two fields form the lookup key. */
unsigned short type; /* This is the 2nd argument to socket(2). 表示套接口字的类型,对于Internet 协议族有三种类型 SOCK_STREAM SOCK_DGRAM SOCK_RAW 对于与应用层socket函数的第二个参数type*/
unsigned short protocol; /* This is the L4 protocol number. */ struct proto *prot; /*套接口网络层口,tcp为tcp_port udp为udp_port 原始套接字为raw_port*/
const struct proto_ops *ops;/* 套接口传输层接口,tcp为inet_stream_ops,udp 为inet_dgram_ops,原始套接字为inet_sockraw_ops*/ unsigned char flags; /* See INET_PROTOSW_* below. */
};
#define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? 端口重用*/
#define INET_PROTOSW_PERMANENT 0x02 /* Permanent protocols are unremovable. 协议不能被替换卸载*/
#define INET_PROTOSW_ICSK 0x04 /* Is this an inet_connection_sock? 是不是为连接类型的接口*/
tcp 不能被替换卸载切为连接型套接字,udp 不能被替换和卸载,rawsocket端口可以重用。
/* Upon startup we insert all the elements in inetsw_array[] into
* the linked list inetsw.
在初始化的时候我们会将上面数组中的的元素按套接字类型插入static struct list_head inetsw[SOCK_MAX];链表数组中
*/
/*
* inetsw_array数组包含三个inet_protosw结构的实例,分别对应
* TCP、UDP和原始套接字。在Internet协议族初始化函数inet_init()中
* 调用inet_register_protosw()将inetsw_array数组中
* 的inet_protosw结构实例,以其type值为key组织到散列表inetsw中,
* 也就是说各协议族中type值相同而protocol值不同的inet_protosw结构
* 实例,在inetsw散列表中以type为关键字连接成链表,通过inetsw
* 散列表可以找到所有协议族的inet_protosw结构实例。
*/ //ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS,在tcp_prot->init中被赋值给inet_connection_sock->icsk_af_ops
static struct inet_protosw inetsw_array[] = //这个和应用层创建套接字相关,个人我理解是属于套接口层,为了把套接口层和传输层衔接起来(tcp_protocol udp_protol icmp_protocol)
{
{
.type = SOCK_STREAM, //在inet_create的时候,用它做为关键字,把下面这几个成员联系在一起
.protocol = IPPROTO_TCP, //tcp_prot udp proto raw_proto头添加到的proto_list中,通过遍历该链表就可以知道有哪些传输层协议添加到该链表中
//协议最终都是通过inet_init中的proto_register添加到proto_list链表中的。family协议族通过sock_register注册
//传输层接口tcp_prot udp_prot netlink_prot等通过proto_register注册
//IP层接口通过inet_add_protocol(&icmp_protocol等注册 ,这些组成过程参考inet_init函数
.prot = &tcp_prot,//传输层操作集 在inet_create中的sk_alloc中赋值
// 先执行ops中的函数,然后执行prot中对应的函数 proto结构为网络接口层,
//结构中的操作实现传输层的操作和从传输层到网络层调用的跳转,
//在proto结构中的某些成员跟proto_ops结构中的成员对应,比如connect()等
.ops = &inet_stream_ops,//套接口层操作集,也就是协议族操作集
// 用来区分协议族(netlink family(ops为netlink_ops)或者 inet family)
// ops在创建套接字的时候被赋值,例如netlink赋值的地方在__netlink_create pf_net赋值的地方在inet_create中
.no_check = 0, //为0表示始终进行校验和操作
.flags = INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
}, {
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,//传输层操作集 在inet_create中的sk_alloc中赋值 先执行ops中的函数,然后执行prot中对应的函数
.ops = &inet_dgram_ops,//套接口层操作集 用来区分协议族(netlink family(ops为netlink_ops)或者 inet family)
// ops在创建套接字的时候被赋值,例如netlink赋值的地方在__netlink_create pf_net赋值的地方在inet_create中
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_PERMANENT,
}, {
.type = SOCK_RAW, //原始套接口
.protocol = IPPROTO_IP, /* wild card */
.prot = &raw_prot,//传输层操作集 在inet_create中的sk_alloc中赋值 先执行ops中的函数,然后执行prot中对应的函数
.ops = &inet_sockraw_ops,//套接口层操作集
//用来区分协议族(netlink family(ops为netlink_ops)或者 inet family) ops在创建套接字的时候被赋值,
//例如netlink赋值的地方在__netlink_create pf_net赋值的地方在inet_create中
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_REUSE,
}
};

3、net_protocol 结构
net_protocol 结构定义了传输层协议(包含icmp igmp协议)以及传输层的报文接收例程,此结构是网络层和传输层之间的桥梁。
/*
* inet_add_protocol函数用于将上述结构的实例(指针)
* 存储到inet_protos 数组中
* update:
* net_protocol是一个非常重要的结构,定义了协议族中支持的传输层协议以及传输层的报文接收实例。此结构是网络层和 传输层之间的桥梁,当网络数据包从网络层流向传输层时,
* 会调用此结构中的传输层协议数据时,会调用此结构中的传输层协议数据报接收处理函数。
*
* 内核中为Internet协议族定义了4个net_protocol结构实例---
* icmp_protocol、udp_protocol、tcp_protocol和igmp_protocol
* ,分别与ICMP、UDP、TCP和IGMP协议一一对应。在Internet协议族
* 初始化时,调用inet_add_protocol()将它们注册到net_protocol
* 结构指针数组inet_protos[MAX_INET_PROTOS]中。在系统运行
* 过程中,随时可以用内核模块加载/卸载方式,调用函数inet_add_protocol()
* /inet_del_protocol()将net_protocol结构实例注册到inet_protos[]数组中,
* 或从中删除。
*///ops = rcu_dereference(inet_protos[proto]);通过该函数获取对应的协议ops
/* This is used to register protocols. */
struct net_protocol {
void (*early_demux)(struct sk_buff *skb);
/* 分组将传递到该函数进行进一步处理*/
/*
* 传输层协议数据包接收处理函数指针,当网络层接收IP数据包
* 之后,根据IP数据包所指示传输层协议,调用对应传输层
* net_protocol结构的该例程接收报文。
* TCP协议的接收函数为tcp_v4_rcv(),UDP协议的接收函数为
* udp_rcv(),IGMP协议为igmp_rcv(),ICMP协议为icmp_rcv()。
*/
int (*handler)(struct sk_buff *skb);
/*
* 在接收到ICMP错误信息并需要传递到更高层时,
* 调用该函数
*/
/*
* 在ICMP模块中接收到差错报文后,会解析差错报文,并根据
* 差错报文中原始的IP首部,调用对应传输层的异常处理
* 函数err_handler。TCP协议为tcp_v4_err(),UDP为
* udp_err(),IGMP则无。
*/
void (*err_handler)(struct sk_buff *skb, u32 info);
/*
* no_policy标识在路由时是否进行策略路由。TCP和UDP默认不进行
* 策略路由。
*/
unsigned int no_policy:1,
netns_ok:1,
/* does the protocol do more stringent
* icmp tag validation than simple
* socket lookup?
*/
icmp_strict_tag_validation:1;
};
初始化后的inet_protos 如下:

4、Internet协议族的初始化
Internet协议初始化函数为inet_init ,通过fs_initcall调用,加载到内核中;
/设备物理层的初始化net_dev_init
TCP/IP协议栈初始化inet_init 传输层的协议初始化也在这里面
传输层初始化proto_init 只是为了显示各种协议用的
套接口层初始化sock_init netfilter_init在套接口层初始化的时候也初始化了
static int __init inet_init(void)
{
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL; sock_skb_cb_check_size(sizeof(struct inet_skb_parm)); rc = proto_register(&tcp_prot, 1);
if (rc)
goto out; rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto; rc = proto_register(&raw_prot, 1);
if (rc)
goto out_unregister_udp_proto; rc = proto_register(&ping_prot, 1);
if (rc)
goto out_unregister_raw_proto; /*
* Tell SOCKET that we are alive...
*/ (void)sock_register(&inet_family_ops); #ifdef CONFIG_SYSCTL
ip_static_sysctl_init();
#endif /*
* Add all the base protocols.
*/
//这里面有每种协议传输层的接收函数,
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
pr_crit("%s: Cannot add ICMP protocol\n", __func__);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
pr_crit("%s: Cannot add UDP protocol\n", __func__);
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif /* Register the socket-side information for inet_create. */
for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r); for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);//把inetsw_array结构中的节点添加到inetsw表中,以type为索引 为套接字层所用 /*
* Set the ARP module up
*/ arp_init(); /*
* Set the IP module up
*/ ip_init(); tcp_v4_init()//创建一个tcp套接字用来发送rst ack 字段 /* Setup TCP slab cache for open requests. */
tcp_init(); /* Setup UDP memory threshold */
udp_init(); /* Add UDP-Lite (RFC 3828) */
udplite4_register(); ping_init(); /*
* Set the ICMP layer up
*/
/*由于协议栈本身有发送ICMP数据报的需求,所以,需要在协议栈中创建内核态的原始套接字,用于发送ICMP数据报,这个事情在协议栈初始化时,
由 icmp_init函数完成。它为每个CPU都创建一个icmp_socket,创建工作由sock_create_kern函数完成,创建流程跟应用层 创建socket完全一致。*/
if (icmp_init() < 0)
panic("Failed to create the ICMP control socket.\n"); /*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
if (ip_mr_init())
pr_crit("%s: Cannot init ipv4 mroute\n", __func__);
#endif if (init_inet_pernet_ops())
pr_crit("%s: Cannot init ipv4 inet pernet ops\n", __func__);
/*
* Initialise per-cpu ipv4 mibs
*/ if (init_ipv4_mibs())
pr_crit("%s: Cannot init ipv4 mibs\n", __func__); ipv4_proc_init(); ipfrag_init(); dev_add_pack(&ip_packet_type); ip_tunnel_core_init(); rc = 0;
out:
return rc;
out_unregister_raw_proto:
proto_unregister(&raw_prot);
out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
goto out;
} fs_initcall(inet_init);
Internet 网络协议族的更多相关文章
- Android网络编程系列 一 TCP/IP协议族
在学习和使用Android网路编程时,我们接触的仅仅是上层协议和接口如Apache的httpclient或者Android自带的httpURlconnection等等.对于这些接口的底层实现我们也有必 ...
- 第12章 网络基础(1)_网络分层和TCP/IP协议族
1. 协议的概念 (1)计算机网络中实现通信必须有一些约定.如对速率.传输代码.代码结构.传输控制步骤和出错控制等约定,这些约定即被称为通信协议 (2)在两个节点之间要成功地进行通信,两个节点之间必须 ...
- 网络基础篇(一)--TCP/IP协议族
TCP/IP协议族是一个分层,多协议通信体系. 1 TCP/IP协议族体系结构 TCP/IP协议族自底而上分为四层: 数据链路层, 网络层, 传输层和应用层. 1.1 数据链路层 实现网卡接口的网络驱 ...
- 网络基础之TCP/IP协议族
一.TCP/IP协议族 1.什么是协议? 计算机与网络设备相互通信依赖于相同的方法,比如,双方通信基于何种语言等.而把通信所依赖的这一切方法统称为规则,而我们就把这种规则称为协议. 协议中存在各种各样 ...
- 基础笔记(三):网络协议之Tcp、Http
目录 一.网络协议 二.TCP(Transmission Control Protocol,传输控制协议) TCP头格式 TCP协议中的三次握手和四次挥手 TCP报文抓取工具 三.HTTP(Hyper ...
- iOS的TCP/IP协议族剖析&&Socket
原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0 简介 该篇文章主要回顾--TCP/IP协议族中的TCP/UDP.HTTP:还有S ...
- TCP/IP协议原理与应用笔记10:TCP/IP协议族
1. 协议族视图如下:(这里我们列举重要的,并不是所有的) (1)网络接入层(数据链路层 和 物理层): 通过接入的物理网络的 功能 和 覆盖范围 进行分析划分为: •LANs :局域网(Local ...
- TCP/IP协议族(一) HTTP简介、请求方法与响应状态码
接下来想系统的回顾一下TCP/IP协议族的相关东西,当然这些东西大部分是在大学的时候学过的,但是那句话,基础的东西还是要不时的回顾回顾的.接下来的几篇博客都是关于TCP/IP协议族的,本篇博客就先简单 ...
- 网络协议UDP
------------------------------互联网协议与协议族------------------------------tcp/ip协议介绍:计算机网络协议,不同类型不同厂家之间的终 ...
随机推荐
- Node.js安装及环境配置 for winer
Node.js安装及环境for Windows 一.安装环境 1.本机系统:Windows 10 Pro(64位) 2.Node.js:v6.9.2LTS(64位) 二.安装Node.js步骤 1.下 ...
- MeteoInfoLab脚本示例:AMSR-E卫星数据投影
AMSR-E(http://nsidc.org/data/amsre/index.html)数据中的Land3数据是HDF-EOS4格式,投影是Cylindrical_Equal_Area.这里示例读 ...
- 分布式系统中的CAP、ACID、BASE概念
目录 CAP ACID BASE CAP 分布式系统中,这三个特性只能满足其中两个. 一致性(Consistency):分布式中一致性又分强一致性和弱一致性,强一致性主浊任何时刻任何节点看到的数据都是 ...
- maven项目导入eclipse报错
错误提示: 原因:未安装maven,缺少ojdbc6.jar包 解决: 一.安装maven 第一步百度搜索Maven官网,进去之后,下载apache-maven-3.5.3-bin.zip,下载完成之 ...
- 《NASA对N+3代先进飞行器概念设计最终报告》阅读笔记
民用航空的爆炸式增长已是未来的必然,灿烂的前景也意味着巨大的挑战,谁能发掘到未来技术的潜力,谁就能称霸下一个30年的天空! 文章目录 总概述 单双争霸 四点发现 方案分析 多维度分析 设计要求 品质因 ...
- Java中的5大队列,你知道几个?
本文已收录至 https://github.com/vipstone/algorithm <算法图解>系列. 通过前面文章的学习<一文详解「队列」,手撸队列的3种方法!>我们知 ...
- Asp.Net Core 查漏补缺《一》 —— IStartFilter
Asp.Net Core 查漏补缺<一> -- IStartFilter IStartFilter 实现了Configure,如下图一,而Configure方法接受并返回Action< ...
- Redis 入门与 ASP.NET Core 缓存
目录 基础 Redis 库 连接 Redis 能用 redis 干啥 Redis 数据库存储 字符串 订阅发布 RedisValue ASP.NET Core 缓存与分布式缓存 内存中的缓存 ASP. ...
- VirtualBox 安装Ubuntu(16.04/18.04)时显示不全的解决方法
是是系统分辨率不同导致的问题 Alt+鼠标左键 (16.04版本亲测有效,18.04版本亲测无效)或者Win+鼠标左键 (18.04版本亲测有效)拖动安装界面,即可显示内容.
- react-native中textInput在androidTV上的焦点处理(坑篇)
react-native中,开发androidTV输入框的焦点处理. 复述流程: 安卓TV上,无法通过上下左右键,以及遥控器的上下左右来获取输入框焦点. 原因: 脸书的锅,但没修,这里官方的说法,Te ...