Linux 网络子系统
今天记录一下Linux网络子系统相关的东西。
因为感觉对这一块还是有一个很大的空白,这件事情太可怕了。
摘抄多份博客进行总结一下Linux网络子系统的相关东西。
一. Linux网络子系统体系结构
Linux 网络体系结构由如下图抽象的形容一下
1 . 用户空间:-----> 应用层
2 . 内核空间:-----> 系统调用接口: 主要指socket 系统调用
-----> 协议无关接口: 实现一组基于socket的通用函数访问各种不同的协议
-----> 网络协议: udp, tcp 协议
-----> 设备无关接口: 将协议与各种网络设备驱动连接在一起。
-----> 设备驱动: 网络体系结构的最底层部分是负责管理物理网络设备的设备驱动层
3 . 硬件: 网络物理设备。
从上往下,形成Linux网络子系统。
二. 重要的头文件以及数据结构介绍
重要的头文件:include/linux/netdevice.h
这里面有个网络子系统的核心数据结构:net_device
net_device
这里面包含了网络设备的各种属性,主要有如下重要属性:
struct net_device
{
char name[IFNAMSIZ];
// 这是设备的名称,包含一个%d格式串,设备注册时,
// 用一个数替换它形成一个新的名称,分配的编号从零开始
//...
unsigned long state;
//记录了设备的状态,是否激活是否是有效是否没有载波等等
unsigned int irq;
/* device IRQ number 中断号 */
unsigned int mtu;
/* interface MTU value 最大传输单元,默认1500 */
unsigned char *dev_addr;
/* hw address, (before bcast
because most packets are
unicast) */
//......还有很多相关的属性。
//关于网络设备的操作结构体
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
}
#define alloc_netdev(sizeof_priv, name, setup) \
alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)\
/* 分配一个net_device结构体可以用上面这个宏函数进行分配
其实最终调用是调用了alloc_netdev_mqs 函数*/
/* 其中,参数sizeof_priv 表示网卡设备的私有数据大小,
name指定网卡设备名称,setup指定网卡设备初始化回调函数 */
//对于不同的网卡设备,kernel提供了更直接的分配函数,
//例如以太网卡,可用用下面的宏函数进行分配:
//include/linux/etherdevice.h
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
//其实最终调用是alloc_etherdev_mqs
//....
//其实这个函数只是做了简单的封装 net/ethernet/eth.c
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
unsigned int rxqs)
{
return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
}
EXPORT_SYMBOL(alloc_etherdev_mqs);
//最终调用还是alloc_netdev_mqs 只不过名字是eth开头,%d会在后面解析出来。
//.......
//ether_setup 函数,以太网初始化函数 net/ethernet/eth.c
/**
* ether_setup - setup Ethernet network device
* @dev: network device
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
EXPORT_SYMBOL(ether_setup);
struct net_device_ops
和字符设备有一定的相似之处,网络设备也有相关的操作函数。
他包含在net_device 结构体中的struct net_device_ops。
他的原型如下:
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
/* 该函数将在设备注册时,
调用(即调用register_netdev()时)来做一些基本的初始化工作。 */
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
/* 该函数在网络设备被激活变为up状态时被调用 */
int (*ndo_stop)(struct net_device *dev);
/* 变为down状态时被调用 */
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
struct net_device *dev);
/* 当上层协议需要发送数据时,调用该函数
需要返回NETDEV_TX_OK 或NET_TX_BUSY来确认结果 */
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb);
/* 呼叫然后决定哪个队列当设备支持多重传输队列的时候 */
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
/* 调用这个函数允许设备接收器更改配置当运行多路传输的时候 */
void (*ndo_set_rx_mode)(struct net_device *dev);
/* 当调用这个函数改变地址过滤
如果驱动没有设置地址过滤,那个这个标志要设置为IFF_UNICAST_FLT */
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
/* 调用设置MAC地址 */
int (*ndo_validate_addr)(struct net_device *dev);
/* 测试多媒体访问地址是否有效 */
int (*ndo_do_ioctl)(struct net_device *dev,
struct ifreq *ifr, int cmd);
/* 当用户请求一个ioctl,如果ioctl没有相对应的接口函数,则返回not supported error code */
int (*ndo_set_config)(struct net_device *dev,
struct ifmap *map);
/* 用它设置网络设备总线接口参数
保留这个接口的原因是因为新的设备要用PCI为了低水平的管理*/
int (*ndo_change_mtu)(struct net_device *dev,
int new_mtu);
/* 使用这个函数该表最大传输单元,
如果没有定义,那么任何请求改变MTU的请求都会报错 */
int (*ndo_neigh_setup)(struct net_device *dev,
struct neigh_parms *);
void (*ndo_tx_timeout) (struct net_device *dev);
/* 当传送器没有任何操作超时调用 */
struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,
struct rtnl_link_stats64 *storage);
struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
/* 上面两个函数必须定义其中一个,这是用户想获取帮助的所调用的 */
void (*ndo_vlan_rx_add_vid)(struct net_device *dev,
unsigned short vid);
/* 当设备支持VLAN过滤的时候,这个函数在一个VLAN ID 被注册时候调用 */
void (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
unsigned short vid);
/* 当设备支持VLAN过滤的时候,这个函数在一个VLAN ID 被注销时候调用 */
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
struct netpoll_info *info);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
/* SR-IOV management functions */
int (*ndo_set_vf_mac)(struct net_device *dev,
int queue, u8 *mac);
int (*ndo_set_vf_vlan)(struct net_device *dev,
int queue, u16 vlan, u8 qos);
int (*ndo_set_vf_tx_rate)(struct net_device *dev,
int vf, int rate);
int (*ndo_set_vf_spoofchk)(struct net_device *dev,
int vf, bool setting);
int (*ndo_get_vf_config)(struct net_device *dev,
int vf,
struct ifla_vf_info *ivf);
int (*ndo_set_vf_port)(struct net_device *dev,
int vf,
struct nlattr *port[]);
int (*ndo_get_vf_port)(struct net_device *dev,
int vf, struct sk_buff *skb);
int (*ndo_setup_tc)(struct net_device *dev, u8 tc);
/* 调用这个函数设置tc 数字关于net设备的传输类别
这个允许网络设备安全的管理队列 */
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
int (*ndo_fcoe_enable)(struct net_device *dev);
/* 当FCoE协议栈使用LLD,所以下面的设备需要配置或者初始化关于FCoE传输的加速。
这时就调用这个函数 */
int (*ndo_fcoe_disable)(struct net_device *dev);
/* 与上面那个函数刚好相反 */
/* .........*/
int (*ndo_fcoe_ddp_setup)(struct net_device *dev,
u16 xid,
struct scatterlist *sgl , unsigned int sgc);
/* 当FCoE初始化想初始一个IO因为有个可能的候选人为直接的数据放置,成功在这个IO执行PPD这个函数返回1, 失败返回0 */
int (*ndo_fcoe_ddp_done)(struct net_device *dev,
u16 xid);
/* 当FCoE目标已经完成,这时候要释放这个资源以便下次请求这个资源 */
int (*ndo_fcoe_ddp_target)(struct net_device *dev,
u16 xid,
struct scatterlist *sgl,
unsigned int sgc);
#endif
#if defined(CONFIG_LIBFCOE) || defined(CONFIG_LIBFCOE_MODULE)
#define NETDEV_FCOE_WWNN 0
#define NETDEV_FCOE_WWPN 1
int (*ndo_fcoe_get_wwn)(struct net_device *dev,
u64 *wwn, int type);
#endif
#ifdef CONFIG_RFS_ACCEL
int (*ndo_rx_flow_steer)(struct net_device *dev,
const struct sk_buff *skb,
u16 rxq_index,
u32 flow_id);
/* 设置硬件过滤为RFS */
#endif
int (*ndo_add_slave)(struct net_device *dev,
struct net_device *slave_dev);
/* 添加一个网络设备的从设备 */
int (*ndo_del_slave)(struct net_device *dev,
struct net_device *slave_dev);
/* 删除 */
u32 (*ndo_fix_features)(struct net_device *dev,
u32 features);
/* 调整请求特征标志通过设备专用的 约束,返回标志结果,不能改变设备的状态*/
int (*ndo_set_features)(struct net_device *dev,
u32 features);
/* 升级设备新的配置 */
};
sk_buff
sk_buff 在include/linux/skbuff.h 里面被声明
/**
* struct sk_buff - socket buffer
* @next: Next buffer in list
* @prev: Previous buffer in list
* @tstamp: Time we arrived
* @sk: Socket we are owned by
* @dev: Device we arrived on/are leaving by
* @cb: Control buffer. Free for use by every layer. Put private vars here
* @_skb_refdst: destination entry (with norefcount bit)
* @sp: the security path, used for xfrm
* @len: Length of actual data
* @data_len: Data length
* @mac_len: Length of link layer header
* @hdr_len: writable header length of cloned skb
* @csum: Checksum (must include start/offset pair)
* @csum_start: Offset from skb->head where checksumming should start
* @csum_offset: Offset from csum_start where checksum should be stored
* @priority: Packet queueing priority
* @local_df: allow local fragmentation
* @cloned: Head may be cloned (check refcnt to be sure)
* @ip_summed: Driver fed us an IP checksum
* @nohdr: Payload reference only, must not modify header
* @nfctinfo: Relationship of this skb to the connection
* @pkt_type: Packet class
* @fclone: skbuff clone status
* @ipvs_property: skbuff is owned by ipvs
* @peeked: this packet has been seen already, so stats have been
* done for it, don't do them again
* @nf_trace: netfilter packet trace flag
* @protocol: Packet protocol from driver
* @destructor: Destruct function
* @nfct: Associated connection, if any
* @nfct_reasm: netfilter conntrack re-assembly pointer
* @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @skb_iif: ifindex of device we arrived on
* @tc_index: Traffic control index
* @tc_verd: traffic control verdict
* @rxhash: the packet hash computed on receive
* @queue_mapping: Queue mapping for multiqueue devices
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport
* ports.
* @dma_cookie: a cookie to one of several possible DMA operations
* done by skb DMA functions
* @secmark: security marking
* @mark: Generic packet mark
* @dropcount: total number of sk_receive_queue overflows
* @vlan_tci: vlan tag control information
* @transport_header: Transport layer header
* @network_header: Network layer header
* @mac_header: Link layer header
* @tail: Tail pointer
* @end: End pointer
* @head: Head of buffer
* @data: Data head pointer
* @truesize: Buffer size
* @users: User count - see {datagram,tcp}.c
*/
//这个结构体里面所有的成员如上注释
//其中重要的有如下几个着重解释一下:
struct sk_buff
{
/* These two members must be first. */
struct sk_buff *next; /* 指向下一个的sk_buff */
struct sk_buff *prev; /* 指向前一个的sk_buff */
struct sock *sk; /* 该sk_buff 拥有的套接字 */
struct net_device *dev; /* 处理该包的设备 */
__be16 protocol; /* 该包所属的协议类型 */
unsigned int len,
data_len; /* 有效数据的长度以及数据总长度 */
sk_buff_data_t transport_header; /* 指向传输层包头 */
sk_buff_data_t network_header; /* 指向网络层包头 */
sk_buff_data_t mac_header; /* 指向链路层包头 */
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail; /* 有效数据的结束 */
sk_buff_data_t end; /* 分配空间的结束 */
unsigned char *head, /* 分配空间的开始 */
*data; /* 有效数据的开始 */
}
//sk_buff中定义了4个指向数据包缓冲区不同位置的指针head、data、tail、end、
//.......
//sk_buff 的基本操作函数
static inline struct sk_buff *__dev_alloc_skb(unsigned int length, <-------------|
gfp_t gfp_mask) |
{ |
struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask); --- |
if (likely(skb)) | |
skb_reserve(skb, NET_SKB_PAD); | |
return skb; | |
} | |
| |
extern struct sk_buff *dev_alloc_skb(unsigned int length); | |
/* | |
struct sk_buff *dev_alloc_skb(unsigned int length) //最开始位置 | |
{ | |
* There is more code here than it seems: | |
* __dev_alloc_skb is an inline | |
*/ | |
return __dev_alloc_skb(length, GFP_ATOMIC); -------------------------------- |
} |
EXPORT_SYMBOL(dev_alloc_skb); |
*/ | |
|
extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev, |
unsigned int length, gfp_t gfp_mask); |
|
|
|
extern struct sk_buff *__alloc_skb(unsigned int size, |
gfp_t priority, int fclone, int node); |
static inline struct sk_buff *alloc_skb(unsigned int size, <—————
gfp_t priority)
{
return __alloc_skb(size, priority, 0, NUMA_NO_NODE); -----
} |
//最终调用__alloc_skb进行申请一个sk_buff结构体 |
//他的函数原型在net/core/skbuff.c |
/** |
* __alloc_skb - allocate a network buffer <____________|
* @size: size to allocate 长度
* @gfp_mask: allocation mask 掩码
* @fclone: allocate from fclone cache instead of head cache
* and allocate a cloned (child) skb
* @node: numa node to allocate memory on
*
* Allocate a new &sk_buff. The returned buffer has no headroom and a
* tail room of size bytes. The object has a reference count of one.
* The return is the buffer. On a failure the return is %NULL.
*
* Buffers may only be allocated from interrupts using a @gfp_mask of
* %GFP_ATOMIC.
*/
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
int fclone, int node)
{
//......
}
Linux 网络子系统的更多相关文章
- Linux网络子系统
再Linux的世界里,万物皆文件,通过虚拟文件系统VFS,程序可以用标准的Linux系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作.下面我们揭示Linux网络子系统的秘密 sockfs ...
- Linux 网络子系统之网络协议接口层(一)
Linux 网络设备驱动之网络协议接口层介绍. 网络协议接口层最主要的功能是给上层协议提供透明的数据包发送和接收接口. 当上层ARP或IP需要发送数据包时,它将调用网络协议接口层的dev_queue_ ...
- linux网络子系统内核分析
1.选择路由 若要将数据包发至PC2,则linux系统通过查询路由表可知168.1.1.10(目的地址)的网关地址为192.168.1.1,此时linux系统选择网卡1发送数据包. 2.邻居子系统(通 ...
- Linux 网络子系统之网络协议接口层(二)
这一篇主要围绕网络协议接口层的发送函数的解析 int dev_queue_xmit(struct sk_buff *skb) 函数解析 声明: /* include/linux/netdevice.h ...
- Linux 网络子系统之结构介绍
Linux 网络设备驱动程序的体系结构 图片说明如下: 网络协议接口层 网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP还是IP,都通过 dev_queue_xmit() 函数 ...
- Linux网络子系统之---- PHY 配置
MII即媒体独立接口,也叫介质无关接口. 它包括一个数据接口,以及一个MAC和PHY之间的管理接口(图1). 数据接口包括分别用于发送器和接收器的两条独立信道.每条信道都有自己的数据.时钟和控制信号. ...
- Linux 网络子系统之NAPI书签
只是一个书签 http://blog.csdn.net/ustc_dylan/article/details/6116334
- linux网络子系统调优
- Linux内核笔记--网络子系统初探
内核版本:linux-2.6.11 本文对Linux网络子系统的收发包的流程进行一个大致梳理,以流水账的形式记录从应用层write一个socket开始到这些数据被应用层read出来的这个过程中linu ...
随机推荐
- play with make
1) the VPATH variable In make world, the VPATH variable guide you where to find the .c/.o file 2) th ...
- window.open和window.location.href的几种用法
windows.open("URL","窗口名称","窗口外观设定"); <A href="javascript:windo ...
- Opencv 完美配置攻略(Win8.1 + Opencv 2.4.8 + VS 2013)
- Android相关学习资料整理
小项目 2015-1-17 记事本 记事本 学习笔记: 结构清晰,简单易懂 2015-1-24 蘑菇街 仿蘑菇街 学习笔记:写博客的一种态度,讲解,配图,代码都很完善,值得学习 相机开发 学习笔记:思 ...
- miRNA特点
### miRNA特点 (1)广泛存在于真核生物中, 是一组不编码蛋白质的短序列RNA,它本身不具有开放阅读框架(ORF),并且由不同于mRNA的独立转录单位表达. (2)通常的长度为20-24 nt ...
- SQL 优化tips
1. 陷阱, 1)几个表进行join,然后过滤 等价于 2)分别过滤为小表后,再join? 并不完全.2)确实比1)效率高,但要注意一些NULL值过滤.否则2)得到的结果比1)多
- java高新技术-操作javaBean
1. 对javaBean的简单内省操作 public class IntroSpectorTest { public static void main(String[] args) throws Ex ...
- Android Studio使用
HelloWorld在虚拟机上正常运行,在红米1S上很抱歉,HelloWorld已停止运行.有时间解决. 在windows下感觉到Android Studio非常慢,急死人.
- Alpha阶段项目展示
1.团队简介 韩青长 前端工程师 我是韩青长,技术小白,抱着对软工的好奇和对未来工作的憧憬选了这门课.暂时选择了测试的工作,也对开发和UI有一定兴趣.从前上帝创造了我们,现在轮到我们来创造自己的软件了 ...
- ID还是普通字段做外键合适?
ORACLE:USER表中没有ID字段,只有USERNAME做为一个唯一一字段当主键, COMMENT评论表中有一个user表的外键是用了USER表中的USERNAME字段. 我总认为这不合理,一般情 ...