随笔-150  评论-223  文章-30  trackbacks-0
 
   ICMP在IP系统间传递差错和管理报文,是任何IP系统必须实现的组成部分。Linux 2.6.34中ICMP模块的实现在linux/icmp.h,net/icmp.h和ipv4/icmp.c中,导出了icmp_err_convert数组和icmp_send函数,供其它网络子系统使用。在其它网络子系统中,当检测到错误时,调用icmp_send产生并发送相应的ICMP差错消息到源主机;当源主机收到ICMP不可达差错消息,传递到原始套接字和传输层,而它们使用icmp_err_convert把对应的消息代码转换成套接字层比较容易理解的错误代码。在内核空间中可发送的ICMP消息包括查询应答和差错报文,下面总结了产生这两类消息的网络子系统(及函数)与错误转换。


应答消息
   应答消息由ICMP模块的内部函数icmp_reply而非icmp_send发送。根据RFC1122 3.2.2.9规范, 除非一个主机作为地址掩码代理,否则不能发送回复,这对应ICMP的icmp_address实现为空,因此上表没有列出地址掩码应答项(内核符号为ICMP_ADDRESSREPLY)。

差错消息
   差错消息由中间路由器或目的主机产生,当数据报不能成功提交给目的主机时。从上表可见,在IP层的接收、本地处理、转发和输出各过程中,都可能产生差错消息;在传输层如果对应的端口没有打开,那么UDP会产生ICMP端口不可达差错,而TCP则会使用自己的差错处理机制发送一个RST复位包,这也是上表没有列出TCP子系统的原因。对于重定向差错,由ICMP模块的icmp_redirect调用ip_rt_redirect更新路由;其它差错则由icmp_unreach处理。


错误转换
   第2列为icmp_err_convert数组索引,第4列也就是调用socket API出错时返回的errno,最后1列为icmp_err_convert中的fatal成员取值,0表示非致命错误,1表示致命错误,需要报告给用户进程。错误转换会被RAW的raw_err、TCP的tcp_v4_err和UDP的udp_err用到,对于ICMP_DEST_UNREACH类型的差错,使用上表转换;ICMP_SOURCE_QUENCH类型的忽略不处理;ICMP_PARAMETERPROB类型的转换成EPROTO(协议错误);ICMP_TIME_EXCEEDED类型的转换成EHOSTUNREACH
   在这要注意,从ICMP_PORT_UNREACH到ECONNREFUSED的转换,不适用于TCP,原因已在上节说明;而对于UDP的未连接套接字,如果主机在线而端口没打开,调用sendto得不到ECONNREFUSED错误,但recvfrom会阻塞,这是因为虽然内核收到了ICMP差错,但没上报给应用进程。尽管如此,如果想得到ECONNREFUSED错误,那么可以写个ICMP守护进程,应用进程先把它的套接字描述符通过unix域套接口传递到ICMP守护进程,而守护进程使用raw socket来接收ICMP差错,再发给应用进程。


发送限速
   不论一般差错消息还是重定向差错消息,发送限速针对的都是特定目标主机。
   一般限速
   在使用icmp_send发送差错消息(PMTU消息除外)时,为减少网络拥塞而限制了发送的速率,限速由xrlim_allow函数实现,定义在ipv4/icmp.c中。
 1#define XRLIM_BURST_FACTOR 6
 2int xrlim_allow(struct dst_entry *dst, int timeout)
 3{
 4    unsigned long now, token = dst->rate_tokens;
 5    int rc = 0;
 6
 7    now = jiffies;
 8    token += now - dst->rate_last;
 9    dst->rate_last = now;
10    if (token > XRLIM_BURST_FACTOR * timeout)
11        token = XRLIM_BURST_FACTOR * timeout;
12    if (token >= timeout) {
13        token -= timeout;
14        rc = 1;
15    }

16    dst->rate_tokens = token;
17    return rc;
18}
   dst为目标路由缓存,timeout为允许发送的超时(单位为jiffies),dst->rate_tokens记录令牌的个数,当令牌个数不小于timeout时,则减少timeout并允许发送一个消息;反之则不能发送,需等到令牌个数累积到大于timeout时才能发送,但是不能无限大,否则就会导致在一个可能很短的timeout内,发送远多于6个的消息,引起ICMP风暴,所以这里限制了令牌的最大值为XRLIM_BURST_FACTOR*timeout即6倍的超时,也就是说在一个timeout内,最多能发送6个差错消息。 
  
   重定向限速
   路由子系统使用ip_rt_send_redirect来发送重定向消息,定义在ipv4/route.c中,该函数内部调用icmp_send实现,在它的限速基础上,使用指数回退算法控制发送速率。
 1void ip_rt_send_redirect(struct sk_buff *skb)
 2{
 3    struct rtable *rt = skb_rtable(skb);
 4    
   
 5
 6    /* No redirected packets during ip_rt_redirect_silence;
 7     * reset the algorithm.
 8     */

 9    if (time_after(jiffies, rt->u.dst.rate_last + ip_rt_redirect_silence))
10        rt->u.dst.rate_tokens = 0;
11
12    /* Too many ignored redirects; do not send anything
13     * set u.dst.rate_last to the last seen redirected packet.
14     */

15    if (rt->u.dst.rate_tokens >= ip_rt_redirect_number{
16        rt->u.dst.rate_last = jiffies;
17        return;
18    }

19
20    /* Check for load limit; set rate_last to the latest sent
21     * redirect.
22     */

23    if (rt->u.dst.rate_tokens == 0 || time_after(jiffies, (rt->u.dst.rate_last (ip_rt_redirect_load << rt->u.dst.rate_tokens)))) {
24        icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
25        rt->u.dst.rate_last = jiffies;
26        ++rt->u.dst.rate_tokens;
27        
28    }

29}
   重定向差错使用ip_rt_redirect_silence(默认为(HZ/50)<<10)、ip_rt_redirect_number(默认为9)和ip_rt_redirect_load(默认为HZ/50)3个量来控制发送的速率;rt->u.dst.rate_last记录上次发送的时间,rt->u.dst.rate_tokens累计发送总数,最大值为ip_rt_redirect_number;当两次发送的时间间隔超过ip_rt_redirect_silence或ip_rt_redirect_load<<rt->u.dst.rate_tokens,并且发送总数不超过ip_rt_redirect_number时,才允许发送一个,这样一来,在ip_rt_redirect_silence间隔内,每次发送的超时呈2的指数增长,达到了变减速发送的效果,直到总数达到ip_rt_redirect_number时停止发送,这是因为源主机可能忽略了重定向消息所以停止发送;当ip_rt_redirect_silence时间过后,又允许发送了,这是因为认为源主机没有更新路由所以又需要发送。
posted @ 2015-05-18 19:52 春秋十二月 阅读(2725) | 评论 (0)编辑 收藏
     摘要:    接上篇初始化与创建,本篇阐述Socket操作和销毁两部分的实现。 Socket操作    系统调用read(v)、write(v)是用户空间读写socket的一种方法,为了弄清楚它们是怎么通过VFS将请求转发到特定协议的实现,下面以read为例(write同理),并假定文件描述符对应的是IPv4 TCP类型的socket...  阅读全文
posted @ 2015-05-03 16:55 春秋十二月 阅读(5137) | 评论 (0)编辑 收藏
     摘要: 引言    在Unix的世界里,万物皆文件,通过虚拟文件系统VFS,程序可以用标准的Unix系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作。对于网络套接字socket也是如此,除了专属的Berkeley Sockets API,还支持一些标准的文件IO系统调用如read(v)、write(v)和close等。那么为什么socket也支持文件IO系统调...  阅读全文
posted @ 2015-05-03 16:31 春秋十二月 阅读(8453) | 评论 (0)编辑 收藏
     摘要: 字符集合     依据RFC3986 2规范,HTTP URI中允许出现的US-ASCII字符的子集,可以分成保留、未保留及转义这几类,每类的全部字符列表如下       ● 保留:  : / ? # [ ] @ ! $ & '( ) * + ,; =共18个,一般...  阅读全文
posted @ 2015-02-10 18:40 春秋十二月 阅读(4650) | 评论 (1)编辑 收藏
     摘要: 脚本概述   nginx是一款著名的开源web服务器,为方便升级与恢复,编写了一个简单的脚本,因为升级备份了可执行文件和配置文件(后缀名为old),所以可用于恢复。当升级时,若nginx正在运行,则不中断服务进行平滑升级,否则直接拷贝覆盖;当恢复时,若nginx正在运行,则不中断服务进行平滑恢复,否则直接拷贝覆盖。是否正在运行根据pid来判断,而pid从pid文件读取...  阅读全文
posted @ 2015-01-19 00:36 春秋十二月 阅读(1996) | 评论 (0)编辑 收藏
     摘要:    本文描述了一种简单的跨平台锁框架的设计与实现,该框架小巧实用、易于扩展,它的特点如下:      ● 实现了线程间互斥锁      ● 实现优化了单线程环境中的空锁和空级别锁      ● 支持编译时或运行时选择锁   ...  阅读全文
posted @ 2014-12-28 23:38 春秋十二月 阅读(2332) | 评论 (6)编辑 收藏
   众所周知,TLS是指线程局部存储,FIFO是Unix中的命名管道,可用于无关进程间的通信,而本文描述的TLS FIFO是指这样一种机制:如果一个线程在每次IO操作时,若没有连接,则先连接到FIFO服务端,再将连接关联到这个线程的TLS中,这里的连接即创建并打开唯一的FIFO,之后的读写就在这个FIFO连接上进行;当FIFO连接断开时,在下次IO操作时会自动重连。这样一来,用户程序就只要调用相关的IO操作,而不必管理连接,极大地简化了程序。使用FIFO通信前先要创建FIFO再打开它,其中创建是最重要的操作,结果有3种情况:成功、失败和已存在。

结构定义
typedef struct 
{
    int fd;
    char *name;
}ipc_fifo_t;
  fd存储FIFO文件描述符,name存储FIFO文件系统路径名。

接口函数
   创建FIFO
ipc_fifo_t* ipc_fifo_make(const char *path,mode_t mode);
  path指定FIFO路径,可以是绝对路径或相对路径,mode指定访问权限,若成功则返回一个FIFO结构,否则为NULL;通常被ipc_fifo_open调用。
   
   打开FIFO
int ipc_fifo_open(ipc_fifo_t **f,const char *path,int flag,mode_t mode);
  flag指定打开标志,如果包含了O_CREAT标志,那么调用ipc_fifo_make创建新的FIFO对象并在打开成功后替换*f,否则如果*f为空,就分配并初始化一个fifo结构;mode指定访问权限,仅当创建时生效。虽然f为输入输出参数,但操作失败时不会影响它,也就是说没有副作用。

  ● 发送数据   
ssize_t ipc_fifo_write(ipc_fifo_t *f,const void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。
   
  ● 接收数据
ssize_t ipc_fifo_read(ipc_fifo_t *f,void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。

  ● 关闭FIFO
void ipc_fifo_close(ipc_fifo_t *f);
  当通信结束的时候,应该调用此函数来关闭FIFO,它会先删除FIFO文件和关闭文件描述符,最后释放fifo结构。

  ● 获取TLS FIFO
ipc_fifo_t* ipc_fifo_tls_get();
  该函数一般被发送数据接口调用,若成功则返回一个FIFO结构,否则为NULL;每个线程对应一个FIFO对象,对于同一线程,获取的是同一个FIFO对象,而后便可调用ipc_fifo_write来发送数据。

工作流程
  创建FIFO
  适用于FIFO客户端和服务端,但服务端由于要异步处理众多FIFO客户端,因此要注意以下2个问题,这也是使用FIFO技术通信的一些细节。
  1)必须以非阻塞读写方式打开知名FIFO,即以O_CREAT|O_RDWR|O_NONBLOCK标志来调用ipc_fifo_open,这样才不会阻塞等待某个客户端以同步写方式打开知名FIFO而返回,因为它使用O_RDWR标志,这样自己既读又写,加上O_NONBLOCK,就立即返回了。
  2)必须以非阻塞只读方式打开对应客户端FIFO,即以O_RDONLY|O_NONBLOCK标志调用ipc_fifo_open,这样就不会阻塞接受客户端建立连接而返回。

  建立FIFO连接
  适用于FIFO客户端,被发送数据接口调用,考虑到服务端可能事先没有打开知名FIFO来监听连接,所以这里先以异步方式写打开知名FIFO如果成功则改以阻塞方式发送唯一路径名到服务端,如果发送完全后,接着以同步方式写打开唯一FIFO,这是为了等待服务端打开了对应的唯一FIFO。

  发送数据

  适用于FIFO客户端,当TLS中没有关联对应的FIFO时,则先调用fifo_tls_get进入建立FIFO连接流程,而后再发数据。
posted @ 2014-12-01 00:13 春秋十二月 阅读(1250) | 评论 (2)编辑 收藏
     摘要:    Web服务器为了支持https访问,通常会使用第三方库openssl实现,而且为了高性能采用异步事件驱动模型,因此连接套接字被设为非阻塞类型,本文在nginx ssl模块的基础上,简化提取它的核心框架,使用面向对象的方式描述,从握手、读写和关闭3个方面进行了分析,由于这3个操作都是异步的,因此操作失败后要调用SSL_get_error来获取错误码,有如下4种情况。 &n...  阅读全文
posted @ 2014-04-11 17:26 春秋十二月 阅读(13543) | 评论 (0)编辑 收藏
模板
    1. 空基类优化
    2. 元编程技术
        2.1. 选择API
        2.2. 计算最值
        2.3. 类型选择
    3. 封装GCC原子操作
    4. 定制类对象的内存管理

算法
    1. 排序
        1.1. 改进的快速排序
        1.2. 原位统计排序     
    2. 多叉树
        2.1. 深度优先存储
        2.2. 迭代器的设计
        2.3. 前序遍历
        2.4. 后序遍历
        2.5. 兄弟遍历
        2.6. 叶子遍历
        2.7. 深度遍历 
    3. 优先级队列
        3.1. 原理
        3.2. 内幕
        3.3. 外观
    4. RSA加解密的证明
    5. DSA数字签名的推导
    6. 基于中国剩余定理优化RSA解密推论的证明
    7. 总结AES加密涉及的数学定理
    8. 为什么素检测存在概率多项式时间算法
    9. Blum数的基本定理及应用

GUI 
    1. MFC中的WM_COMMAND传递
    2. ATL和WTL中的消息反射
    3. 工作线程与消息循环
    4. 多窗口的组合与分离
        4.1. 接口
        4.2. 实现

跨平台
    1. 用户态自旋锁
    2. 互斥锁
    3. 信号量
    4. socket管道
    5. 锁框架的设计与实现

网络
    1. 运用状态机异步接收变长包
    2. 基于OpenSSL实现的安全连接
    3. TCP/IP FAQ
        3.1. 链路层、网络层和传输层
        3.2. 插口层和应用层
    4. Linux套接字与虚拟文件系统
        4.1. 初始化和创建
        4.2. 操作和销毁
    5. Linux ICMP消息的产生与转换
    6. nginx iocp
        6.1. tcp异步连接
        6.2. udp异步接收
        6.3. scm服务控制
    7. TCP分组丢失时的状态变迁
    8. 基于ENet实现可靠UDP通信的同步模型

Shell应用
    1. 自动生成并安装服务脚本
    2. nginx升级与恢复
    3. 使用awk定位反汇编输出
    4. 自动化批量编译
posted @ 2014-04-10 16:04 春秋十二月 阅读(1832) | 评论 (0)编辑 收藏
   为了方便更改系统tcp内核的一些参数,编写ktcpopt脚本如下
1#! /bin/bash 
2# ktcpopt 
3
4sed -i '/net.ipv4.tcp_syncookies\|net.ipv4.tcp_tw_reuse\|net.ipv4.tcp_tw_recycle\|net.ipv4.tcp_fin_timeout\|net.ipv4.tcp_max_syn_backlog\|net.ipv4.tcp_max_tw_buckets\|net.ipv4.ip_local_port_range/d' /etc/sysctl.conf
5
6sed -i '$a\net.ipv4.tcp_syncookies=1\nnet.ipv4.tcp_tw_reuse=1\nnet.ipv4.tcp_tw_recycle=1\nnet.ipv4.tcp_fin_timeout=30\nnet.ipv4.tcp_max_syn_backlog=8192\nnet.ipv4.tcp_max_tw_buckets=5000\nnet.ipv4.ip_local_port_range=10000 65000' /etc/sysctl.conf

  为了方便配置程序的崩溃调试,编写coredump脚本如下
1#! /bin/bash
2# coredump
3
4sed -i '/ulimit -c unlimited\|export core_path=\/tmp\/corefiles\|mkdir -p $core_path\|echo "0" > \/proc\/sys\/kernel\/core_uses_pid\|echo "$core_path\/%e" > \/proc\/sys\/kernel\/core_pattern/d' ~/.bashrc
5
6sed -i '$a\ulimit -c unlimited\nexport core_path=/tmp/corefiles\nmkdir -p $core_path\necho "0" > /proc/sys/kernel/core_uses_pid\necho "$core_path/%e" > /proc/sys/kernel/core_pattern' ~/.bashrc

   从以上2个脚本可以看出,为避免每次调用脚本时增加重复行以致配置文件逐渐变大,先删除已经存在的相关配置,再在末尾增加。这里使用了sed来实现直接在文件中删除和增加,其中由于删除操作的匹配模式有多个,因此使用了|符号并用\转义,并且第2个脚本模式文本中含有/符号,这与sed本身的模式限定符/存在歧义,因此也用了\转义;增加使用$a\命令来实现在文件末尾增加配置,并用\n换行。
posted @ 2014-03-24 18:44 春秋十二月 阅读(1317) | 评论 (0)编辑 收藏
仅列出标题
共15页: First 5 6 7 8 9 10 11 12 13 Last