分类目录归档:LINUX

关于linux使用的知识

zabbix 配置发邮件 sendmail

这段时间在研究zabbix(2.0.3) ,我把遇到的一些问题说一下,这个文档是关于用系统自带的sendmail发邮件的sendmail的安装过程

yum install sendmail
关于sendmail的配置
# cat /etc/hosts
127.0.0.1       zabbix.kumouse.com    localhost.localdomain    localhost

# cat /etc/mail/access
Connect:localhost.localdomain           RELAY
Connect:localhost                       RELAY
Connect:127.0.0.1                       RELAY
Connect:zabbix.kumouse.com              RELAY

# cat /etc/mail/local-host-names
zabbix.kumouse.com

改完成后,重启sendmail服务,通过telnet命令,测试一下看看是否已生效
# telnet 127.0.0.1 25
Trying 127.0.0.1…
Connected to zabbix.kumouse.com (127.0.0.1).
Escape character is ‘^]’.
220 monitor.bot.cms ESMTP Sendmail 8.13.8/8.13.8; Sun, 10 Oct 2010 14:00:33 +0800

     

localhost.localdomain 已经变成了 zabbix.kumouse.com 这个域名,表示修改成功

接下来就可以在zabbix 的管理页面上进行Email .admintration->media types

设置一下然后设置一个configureation -> actions 加入一个行动

然后给triggers被触发了你就是收到邮件了

关于mysql的模式匹配

MySQL提供标准的SQL模式匹配,以及一种基于象Unix实用程序如vi、grep和sed的扩展正则表达式 模式匹配的格式。 SQL的模式匹配允许你使用“_”匹配任何单个字符,而“%”匹配任意数目字符(包括零个字符)。 在 MySQL中,SQL的模式缺省是忽略大小写的。下面显示一些例子。注意在你使用SQL模式时,你不 能使用=或!=;而使用LIKE或NOT LIKE比较操作符。 为了找出以“b”开头的名字:
mysql> SELECT * FROM pet WHERE name LIKE “b%”;

为了找出以“fy”结尾的名字:
mysql> SELECT * FROM pet WHERE name LIKE “%fy”; 为了找出包含一个“w”的名字:
mysql> SELECT * FROM pet WHERE name LIKE “%w%”;

为了找出包含正好5个字符的名字,使用“_”模式字符:
mysql> SELECT * FROM pet WHERE name LIKE “_____”;

由MySQL提供的模式匹配的其他类型是使用扩展正则表达式。当你对这类模式进行匹配测试时,使用 REGEXP和NOT REGEXP操作符(或RLIKE和NOT RLIKE,它们是同义词)。 扩展正则表达式的一些字符是: “.”匹配任何单个的字符。
一个字符类“[…]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”、“b”或“c”。
为了命名字符的一个范围,使用一个“-”。“[a-z]”匹配任何小写字母,而“[0-9]”匹配任
何数字。
“ * ”匹配零个或多个在它前面的东西。例如,“x*”匹配任何数量的“x”字符,“[0-9]*”
匹配的任何数量的数字,而“.*”匹配任何数量的任何东西。
正则表达式是区分大小写的,但是如果你希望,你能使用一个字符类匹配两种写法。例如,
“[aA]”匹配小写或大写的“a”而“[a-zA-Z]”匹配两种写法的任何字母。
如果它出现在被测试值的任何地方,模式就匹配(只要他们匹配整个值,SQL模式匹配)。
为了定位一个模式以便它必须匹配被测试值的开始或结尾,在模式开始处使用“^”或在模式的
结尾用“$”。
为了说明扩展正则表达式如何工作,上面所示的LIKE查询在下面使用REGEXP重写: 为了找出以“b”开头的名字,使用“^”匹配名字的开始并且“[bB]”匹配小写或大写的“b”:
mysql> SELECT * FROM pet WHERE name REGEXP “^[bB]”;

为了找出以“fy”结尾的名字,使用“$”匹配名字的结尾:
mysql> SELECT * FROM pet WHERE name REGEXP “fy$”;

为了找出包含一个“w”的名字,使用“[wW]”匹配小写或大写的“w”:
mysql> SELECT * FROM pet WHERE name REGEXP “[wW]”;

既然如果一个正规表达式出现在值的任何地方,其模式匹配了,就不必再先前的查询中在模式的两
方面放置一个通配符以使得它匹配整个值,就像如果你使用了一个SQL模式那样。
为了找出包含正好5个字符的名字,使用“^”和“$”匹配名字的开始和结尾,和5个“.”实例在
两者之间:
mysql> SELECT * FROM pet WHERE name REGEXP “^…..$”;

你也可以使用“{n}”“重复n次”操作符重写先前的查询:
mysql> SELECT * FROM pet WHERE name REGEXP “^.{5}$”;

查找数字和其他的模糊查询语句
Select * from pet where name REGEXP “[^a-zA-Z].”;

lnmp 502 bad gateway 错误解决方法

我靠,lnmp环境,1个多月502都3次了。网上找了找解决办法。如下

 

解决方法:
可以尝试根据lnmp一键安装包中的脚本手动安装一下,看看是什么错误导致的,在网上搜索一下,或者把错误信息发上来。如果实在不会提供按http://lnmp.org/install.html这个安装时的lnmp.log日志文件(可以用winscp登陆下载lnmp.log,并上传到本论坛),没有错误信息我们没法说什么原因。

第二种原因:
在php.ini里,eaccelerator配置项一定要放在Zend Optimizer配置之前,否则也可能引起502 Bad Gateway

第三种原因:
在安装好使用过程中出现502问题,一般是因为默认php-cgi进程是5个,可能因为phpcgi进程不够用而造成502,需要修改/usr/local/php/etc/php-fpm.conf 将其中的max_children值适当增加。
也有可能是max_requests值不够用。

第四种原因:
php执行超时,修改/usr/local/php/etc/php.ini 将max_execution_time 改为300

第五种原因:
磁盘空间不足,如mysql日志占用大量空间

第六种原因:
查看php-cgi进程是否在运行

第七种原因:
将nginx.conf里的
fastcgi_connect_timeout
fastcgi_send_timeout
fastcgi_read_timeout都调大一点。

其他可能原因:http://bbs.vpser.net/thread-1654-1-1.html

九、也可以尝试将unix套接字改成tcp/ip的,修改/usr/local/php/etc/php-fpm.cnf 里设置<value name=“listen_address”>/tmp/nginx.socket</value> 改成<value name=“listen_address”>127.0.0.1:9000</value> ,同时/usr/local/nginx/conf/nginx.conf 及其/usr/local/nginx/conf/vhost/ 下面的虚拟主机配置里的fastcgi_pass  unix:/tmp/php-cgi.sock; 替换为fastcgi_pass  127.0.0.1:9000;  之后重启试试。

十、如果虚拟主机的日志文件过大也可能会造成502问题。
建议定期清空一下虚拟主机的日志文件。

调大以下两个参数(适当调整,否则危险.)

<value name=”max_children”>10</value>
<value name=”request_terminate_timeout”>60s</value>
max_children最多10个进程,按照每个进程20MB内存,最多200MB。
request_terminate_timeout执行的时间为60秒,也就是1分钟。

丢失mysql密码 如何重置 重置mysql密码

今天无事在php.org闲逛,无意中发现了mysql数据库root密码丢失后的恢复方法,不敢独享,贡献出来希望对大家有帮助。

1、结束当前正在运行的mysql进程。 # killall mysqld

2、用mysql安全模式运行并跳过权限验证。 # /usr/bin/safe_mysqld –skip-grant-tables

3、以root身份登录mysql。 # mysql -u root

4、修改root用户口令。 mysql>; use mysql; mysql>; update user set Password = PASSWORD(‘pass’) where User =’root’; Query OK, 2 rows affected (0.02 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql>;exit

5、结束mysql安全模式,用正常模式运行mysql。 # killall mysqld # mysqld 6、试试你新修改的口令,嘿嘿……屡试屡爽! 今天无事在php.org闲逛,无意中发现了mysql数据库root密码丢失后的恢复方法,不敢独享,贡献出来希望对大家有帮助。 1、结束当前正在运行的mysql进程。 # killall mysqld 2、用mysql安全模式运行并跳过权限验证。 # /usr/bin/safe_mysqld –skip-grant-tables 3、以root身份登录mysql。 # mysql -u root 4、修改root用户口令。 mysql>; use mysql; mysql>; update user set Password = PASSWORD(‘pass’) where User =’root’; Query OK, 2 rows affected (0.02 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql>;exit 5、结束mysql安全模式,用正常模式运行mysql。 # killall mysqld # mysqld 6、试试你新修改的口令,嘿嘿……屡试屡爽! 今天无事在php.org闲逛,无意中发现了mysql数据库root密码丢失后的恢复方法,不敢独享,贡献出来希望对大家有帮助。

1、结束当前正在运行的mysql进程。 # killall mysqld

2、用mysql安全模式运行并跳过权限验证。 # /usr/bin/safe_mysqld –skip-grant-tables

3、以root身份登录mysql。 # mysql -u root

4、修改root用户口令。 mysql>; use mysql; mysql>; update user set Password = PASSWORD(‘pass’) where User =’root’; Query OK, 2 rows affected (0.02 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql>;exit

5、结束mysql安全模式,用正常模式运行mysql。 # killall mysqld # mysqld

6、试试你新修改的口令,嘿嘿……屡试屡爽!

1、结束当前正在运行的mysql进程。 # killall mysqld

2、用mysql安全模式运行并跳过权限验证。 # /usr/bin/safe_mysqld –skip-grant-tables

3、以root身份登录mysql。 # mysql -u root

4、修改root用户口令。 mysql>; use mysql; mysql>; update user set Password = PASSWORD(‘pass’) where User =’root’; Query OK, 2 rows affected (0.02 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql>;exit

5、结束mysql安全模式,用正常模式运行mysql。 # killall mysqld # mysqld

6、试试你新修改的口令,嘿嘿……屡试屡爽!

安装debian 遇到的无线网卡的问题

昨天安装了 debian 发现只能使用有线网卡上网,无线无法使用,用lsmod看也挂载了RT73的模式,但是dmesg后发现
[   63.664048] firmware: requesting rt73.bin
[   63.670027] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[   68.831346] firmware: requesting rt73.bin
[   68.842955] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[   80.756466] firmware: requesting rt73.bin
[   80.790575] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[   90.955321] firmware: requesting rt73.bin
[   90.964185] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  102.940896] firmware: requesting rt73.bin
[  102.953273] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  113.139790] firmware: requesting rt73.bin
[  113.152929] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  125.136884] firmware: requesting rt73.bin
[  125.203243] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  135.245554] firmware: requesting rt73.bin
[  135.258749] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  147.276858] firmware: requesting rt73.bin
[  147.289446] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  157.326490] firmware: requesting rt73.bin
[  157.339120] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  169.344628] firmware: requesting rt73.bin
[  169.360210] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  179.448775] firmware: requesting rt73.bin
[  179.464593] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  191.510340] firmware: requesting rt73.bin
[  191.521165] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  201.543051] firmware: requesting rt73.bin
[  201.555175] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  313.860291] firmware: requesting rt73.bin
[  313.872983] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  323.920712] firmware: requesting rt73.bin
[  323.935466] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  436.388172] firmware: requesting rt73.bin
[  436.399077] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  446.427830] firmware: requesting rt73.bin
[  446.440847] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  472.808760] firmware: requesting rt73.bin
[  472.816789] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  494.946043] firmware: requesting rt73.bin
[  494.954865] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  517.091645] firmware: requesting rt73.bin
[  517.102763] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  539.174906] firmware: requesting rt73.bin
[  539.186079] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  558.847040] firmware: requesting rt73.bin
[  558.860371] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  561.261524] firmware: requesting rt73.bin
[  561.274506] phy0 -> rt2x00lib_request_firmware: Error – Failed to request Firmware.
[  568.897654] firmware: requesting rt73.bin
[  568.978907] ADDRCONF(NETDEV_UP): wlan0: link is not ready

用桌面工具也搜不到无线网,手工加入也是上不了
ifup wlan0显示
Ignoring unknown interface wlan0=wlan0

iwlist wlan0 scanning
显示网卡没有启动

这里有点晕菜了,后来发现应该是驱动的问题

下边说说解决的过程
我下载的的debian5.08 i386 DVD 第一张盘
安装后并无iwlist之类的命令,要自己安
aptitude install frimware-iwlwifi wireless-tools
安完后
再安装雷凌网卡的驱动包就OK了
aptitude install firmware-ralink
然后就看到nm-applet搜出了无线信号
问题解决了

LINUX TTL软件 Minicom – linux下的超级终端

最近由于一些原因,要把系统转到linux,于是就安了fedora14(出的好快,我记得几个月不看,就到14了,几个月前还只有13测试版的)

但是我在调试路由用的TTL怎么使用呢,在google上一查,发现有个软件叫minicom,等同于winodows下的超级终端

于是我就yum install minicom ,没想到在默认的源里就有。

然后简单设置一下就可以正常使用了。

linux下要先看看,系统是否认出了设备(我使用的是PL2303心片的USB转TTL)

运行命令 dmesg | grep usb

看看是否认出了一个叫PL2303的设备,这里同时也是设备名,但是有时候是不准确的比如/dev/ttyUSB0

看具体的设备,最好直接到/dev/下看看,有时候是ttyUSB0有时确是1,这个要看清,并记下

然后进入minicom这个软件的配置页

命令 minicom -s

选第三项

 

按相应的字母,就可以改变对应的选项

改好后保存并退出。它会自动进入工作状态。

如果要退出,按CTRL+Z,再按Q回车

。。。。。。。。中间少了点东西是选项的英文菜单,等我在工具环境中再加入。

fedora13 安装 SpoonWep2 心得

今天在fedora13下破解wep,懒得总是复制MAC了,想能不能在fedora上也安装上SpoonWep2呢,找了半天也没有rpm的包.后来发现直接把deb的包解出来,把文件直接复制到系统对应目录就可以运行了,很简单嘛:) .后来又发现问题了.在shell中运行,找不到网出现这个错误 /usr/local/bin/wifispoonfeeder/spoonwep/tmp/wscapture-01.txt 在网上找找了,发现在出现这个错误时,再打开一个shell运行ln -s /usr/local/bin/wifispoonfeeder/spoonwep/tmp/wscapture-01.csv /usr/local/bin/wifispoonfeeder/spoonwep/tmp/wscapture-01.txt 命令就可以解决了,但是每次搜网都要运行一次.

spoonwep-2-1-i686.pkg.tar

IP 选路表 和 选路算法

1 引言
讨论选择路由的细节,选路表的结构,以及实现算法,路由如何使用子网掩码,如何区分网络路由子网路由以及主机路由

2 路由维护和查找
应当选择能使查找路由花费最小的IP数据结构和算法,维护路由的花费则并不那么重要
利用桶散列(bucket hashing)迅速找到正确的散列表元

3 选路表结构
保存路由的主要数据结构是数组。数组的每个元素对应一个散列表元,并包含一个指针,指向被装入这个散列表元中的通主目的站的路由记录链表。表中每个记录包含一个IP目的地址,子网掩码,下一跳地址,用于向下一跳地址发送数据的网络接口,以及其他的路由管理中使用的信息。由于IP预先并不知道子网掩码,它公使用IP目的地址的网络部分计算散列函数,在一个链表中查找表项时,使用的是完整的目的地址来进行比较。

4 选路表数据结构

/* route.h – RTFREE */

/* 路由表项 */
struct route {
IPaddr rt_net; /* 路由的目的地址 */
IPaddr rt_mask; /* 路由的掩码 */
IPaddr rt_gw; /* 下一跳的IP */
u_short rt_metric; /* 距离度量 */
u_short rt_ifnum; /* 接口的序号 */
short rt_key; /* 排序关键字 */
short rt_ttl; /* 生存时间(秒) */
struct route *rt_next; /* 哈希值指向下一个条目 */
/* stats */
int rt_refcnt; /* 引用计数 */
int rt_usecnt; /* 记录该路由使用的次数 */
};

/* 路由表的全局数据 */
struct rtinfo {
struct route *ri_default; /* 包含默认路由的下一跳地址 */
int ri_bpool; /* 结点的地址??? Route.ri_bpool = mkpool(sizeof(struct route), RT_BPSIZE) 现在还不太明白 */
Bool ri_valid; /* 此路由的数据结构是否被初始化 */
int ri_mutex; /* 互斥信号 */
};

#define RT_DEFAULT ip_anyaddr /* 默认的网络地址 全0 */
#define RT_LOOPBACK ip_loopback /* 回环地址 */
#define RT_TSIZE 512 /* 散列表长度??-these are pointers; it’s cheap */
#define RT_INF 999 /* 路由永不超时 */

#define RTM_INF 16 /* 一个最大距离度量 – an infinite metric */

/* rtget()的第二个参数… */

#define RTF_REMOTE 0 /* 远程主机产生的通信 */
#define RTF_LOCAL 1 /* 本地产生的通信 */

#define RT_BPSIZE 100 /* 路由的最大数量 */

/* RTFREE – 删除一个路由引用(假定持有ri_mutex) */

#define RTFREE(prt)
if (–prt->rt_refcnt <= 0) { freebuf(prt); } extern struct rtinfo Route; extern struct route *rttable[]; int rtadd(IPaddr, IPaddr, IPaddr, unsigned, unsigned, unsigned); int rtdel(IPaddr, IPaddr), rthash(IPaddr), rtfree(struct route *); struct route *rtget(IPaddr, Bool); void rtinit(void); void ipredirect(struct ep *, unsigned, struct route *); void ipdbc(unsigned, struct ep *, struct route *); 5 路由的生成源及保持时间 选路表项的易失性与它的来源有关(从备份存储器中得到,ICMP或选路协议,网络管理员手动操作),但为了排除网络中的路由问题,管理员可以推翻任何路由并建立不可改变的永久的路由 6 为数据报选择路由 6.1 实用过程 /* netnum.c - netnum */ #include
#include
#include

/*————————————————————————
* netnum – 计算一个给定IP 地址的网络部分
*————————————————————————
*/
IPaddr
netnum(IPaddr ipa)
{
IPaddr mask = ~0;

if (IP_CLASSA(ipa)) mask = hl2net(0xff000000);
if (IP_CLASSB(ipa)) mask = hl2net(0xffff0000);
if (IP_CLASSC(ipa)) mask = hl2net(0xffffff00);
return ipa & mask; /* 返回一个主机地址字节为全0的地址*/
}

/* netmatch.c – netmatch */

#include
#include
#include

/*————————————————————————
* netmatch – 是否是这个网络上的地址?
*————————————————————————
*/
Bool
netmatch(IPaddr dst, IPaddr net, IPaddr mask, Bool islocal)
{
if ((dst & mask) != (net & mask)) /* 目的地址与选路表中地址比较*/
return FALSE;
/*
* 源地址为本地可以只匹配单播地址 (主机为路由)
*/
if (islocal) /* 如果是本地*/
if (isbrc(dst) || IP_CLASSD(dst)) /* 如果目的地址不是广播地址,或不是D类*/
return mask != ip_maskall; /* 并且掩码不为全1,则返回真,否则为假*/
return TRUE;
}

/* netmask.c – netmask */

#include
#include
#include

IPaddr netnum(IPaddr);

/*————————————————————————
* netmask – 为给定网络设定默认掩码
*————————————————————————
*/
IPaddr
netmask(IPaddr net)
{
IPaddr netpart;
int i;

if (net == 0) /* 目的地址为全0,则使用默认路由,返回全0掩码*/
return net;
/* 网络匹配检查 (子网) */

netpart = netnum(net); /* 提取出网络部分*/
for (i=0; i
#include
#include

/*————————————————————————
* rthash – 计算网络的哈希值
*————————————————————————
*/
int
rthash(IPaddr net)
{
int bc = IP_ALEN; /* # 字节数 */
unsigned int hv = 0; /* 哈希值 */

if (IP_CLASSA(net)) bc = 1;
else if (IP_CLASSB(net)) bc = 2;
else if (IP_CLASSC(net)) bc = 3;
else if (IP_CLASSD(net))
return ((net>>24) & 0xf0) % RT_TSIZE; /* D类前4比特乘16再与散列表长度取余数*/
while (bc–)
hv += ((char *)&net)[bc] & 0xff; /* 每8位相加*/
return hv % RT_TSIZE; /* 返回除以散列长度后的余数*/
}

6.2 获得一个路由

/* rtget.c – rtget */

#include
#include
#include

/*————————————————————————
* rtget – 获得一个给定的IP地址的路由
*————————————————————————
*/
struct route *
rtget(IPaddr dest, Bool local)
{
struct route *prt;
int hv;

if (!Route.ri_valid) /* 如果路由的数据结构没被初始化则应该先初始化*/
rtinit();
wait(Route.ri_mutex); /* 等待信号互斥量*/
hv = rthash(dest); /* 计算哈希值*/
for (prt=rttable[hv]; prt; prt=prt->rt_next) {
if (prt->rt_ttl <= 0) continue; /* 路由过期 */ if (netmatch(dest, prt->rt_net, prt->rt_mask, local)) /* 如果网络地址匹配*/
if (prt->rt_metric < RTM_INF) /* 并且距离量度小于最大的量度*/ break; /* 则退出for循环*/ } if (prt == 0) /* 如果没有找到匹配的网络地址*/ prt = Route.ri_default; /* 使用默认路由 */ if (prt != 0 && prt->rt_metric >= RTM_INF) /*如果找到匹配且路离量度大于最大的量度*/
prt = 0; /* prt指向0*/
if (prt) {
prt->rt_refcnt++; /* 引用计数加1*/
prt->rt_usecnt++; /* 统计计数加1*/
}
signal(Route.ri_mutex); /* 释放互斥量*/
return prt;
}

6.3 数据结构初始化

/* rtinit.c – rtinit */

#include
#include
#include
#include

struct rtinfo Route;
struct route *rttable[RT_TSIZE];

/*————————————————————————
* rtinit – 初始化路由表
*————————————————————————
*/
void
rtinit(void)
{
int i;

for (i=0; i
#include
#include #include

extern Bool dorip; /* TRUE 如果我们正在运行的RIP输出 */
extern int rippid; /* 输出的RIP的pid,如果运行 */

/*————————————————————————
* rttimer – 更新TTLS及删除过期路由
*————————————————————————
*/
void
rttimer(unsigned int delta)
{
struct route *prt, *prev;
Bool ripnotify;
int i;

if (!Route.ri_valid) /* 路由未被初始化则返回*/
return;
wait(Route.ri_mutex); /* 等待互斥量*/

ripnotify = FALSE; /* RIP通知关闭*/
for (i=0; irt_ttl != RT_INF) /* 如果生存时间不等于永久*/
prt->rt_ttl -= delta; /* 生存时间减去相应的时间*/
if (prt->rt_ttl <= 0) { /* 如果生存时间小于等于0*/ #ifdef RIP /* 条件编译,当打开RIP时*/ if (dorip && prt->rt_metric < RTM_INF) { /*RIP打开且距离小于最大距离*/ prt->rt_metric = RTM_INF; /* 设置距离为最大距离*/
prt->rt_ttl = RIPZTIME; /* 生存时间为无穷*/
ripnotify = TRUE; /* 打开RIP通知*/
continue;
}
#endif /* RIP */
if (prev) { /*如果prev不为空,删除prt指向的表项*/
prev->rt_next = prt->rt_next;
RTFREE(prt);
prt = prev->rt_next;
} else { /*否则说明prt就是第一个表项*/
rttable[i] = prt->rt_next; /*删除第一个表项*/
RTFREE(prt);
prt = rttable[i];
}
continue;
}
prev = prt; /* 指向下一个*/
prt = prt->rt_next; /* 指向下一个*/
}
}
prt = Route.ri_default; /* 使用默认路由*/
if (prt && (prt->rt_ttlrt_ttl -= delta) <= 0) { /* 默认路由不合适*/ #ifdef RIP if (dorip && prt->rt_metric < RTM_INF) { /* RIP打开且距离小于最大距离 */ prt->rt_metric = RTM_INF; /* 距离为最大距离量度 */
prt->rt_ttl = RIPZTIME; /* 生存时间为无穷*/
} else
#endif /* RIP */
{
RTFREE(Route.ri_default); /* 释放默认路由 */
Route.ri_default = 0; /* 清除默认路由指针*/
}
}
signal(Route.ri_mutex); /* 释放互斥量*/
#ifdef RIP
if (dorip && ripnotify) /* RIP打开并且RIP通知打开*/
send(rippid, 0); /* 发送任何东西,但超时 */
#endif /* RIP */
return;
}

7.1 增加路由

/* rtadd.c – rtadd */

#include
#include
#include #include

struct route *rtnew(IPaddr, IPaddr, IPaddr, unsigned,unsigned,unsigned);
void rtinit(void);
int rthash(IPaddr);

/*————————————————————————
* rtadd – 添加一个路由到路由表
*————————————————————————
*/
int
rtadd(IPaddr net, IPaddr mask, IPaddr gw, unsigned metric,
unsigned intf, unsigned ttl)
{
struct route *prt, *srt, *prev;
Bool isdup;
int hv, i;

if (!Route.ri_valid) /* 初始化*/
rtinit();

prt = rtnew(net, mask, gw, metric, intf, ttl); /* 建立新的结点*/
if (prt == (struct route *)SYSERR) /* 分配失败则返回错误*/
return SYSERR;

/* 计算,路由在队列中的键 */
prt->rt_key = 0;
for (i=0; i<32; ++i) prt->rt_key += (mask >> i) & 1;
wait(Route.ri_mutex); /* 等待互斥量*/

/* 默认路由的特殊情况 */
if (net == RT_DEFAULT) { /* 如果网络地址为任意,全0*/
if (Route.ri_default) /* 默认路由不为空*/
RTFREE(Route.ri_default); /* 释放默认路由*/
Route.ri_default = prt; /* 用新的路由代替*/
signal(Route.ri_mutex); /* 释放互斥量*/
return OK;
}
prev = NULL;
hv = rthash(net); /* 计算哈希值*/
isdup = FALSE; /* 是否有相同地址的路由表项*/
/* 查找是否有相同的路由*/
for (srt=rttable[hv]; srt; srt = srt->rt_next) { /*srt 为空则退出for*/
if (prt->rt_key > srt->rt_key) /* */
break;
if (srt->rt_net == prt->rt_net && /* 网络地址和掩码均相同,找到相同的地址的路由*/
srt->rt_mask == prt->rt_mask) {
isdup = TRUE; /* isdup为真*/
break;
}
prev = srt;
}
if (isdup) { /* isdup为真,说明有相同的路由表项*/
struct route *tmprt;

if (srt->rt_gw == prt->rt_gw) { /* 源路由表与新路由表的下一跳相同 */
/* 更新新由表 */
#ifdef RIP /* RIP打开的情况,上边说过很多回了,下边不再多说*/
if (dorip) {
srt->rt_ttl = ttl;
if (srt->rt_metric != metric) {
if (metric == RTM_INF)
srt->rt_ttl = RIPZTIME;
send(rippid, 0);
}
}
#endif /* RIP */
srt->rt_metric = metric; /* 更新距离量度*/
RTFREE(prt); /* 释放prt*/
signal(Route.ri_mutex); /* 释放互斥量*/
return OK;
}
/* else, someone else has a route there… */
if (srt->rt_metric <= prt->rt_metric) { /* 源路由比新路由具有更小的距离并且下一跳地址不同*/
/* 没有更好的路由线路,放弃新的路由 */

RTFREE(prt);
signal(Route.ri_mutex); /* 释放互斥量*/
return OK;
}
#ifdef RIP
else if (dorip)
send(rippid, 0);
#endif /* RIP */
tmprt = srt; /* 执行到这里说明新路由更优,释放旧的路由*/
srt = srt->rt_next;
RTFREE(tmprt);
}
#ifdef RIP
else if (dorip)
send(rippid, 0);
#endif /* RIP */
prt->rt_next = srt; /* 插入新的路由*/
if (prev) /* 插入到链表的中间*/
prev->rt_next = prt;
else /* 插入到链表的开始*/
rttable[hv] = prt;
signal(Route.ri_mutex);
return OK;
}

建立新的结点,很简单不多说了

/* rtnew.c – rtnew */

#include
#include
#include

/*————————————————————————
* rtnew – 创建一个路由结构
*————————————————————————
*/
struct route *
rtnew(IPaddr net, IPaddr mask, IPaddr gw, unsigned metric,
unsigned ifnum, unsigned ttl)
{
struct route *prt;

prt = (struct route *)getbuf(Route.ri_bpool);
if (prt == (struct route *)SYSERR) {
IpRoutingDiscards++;
return (struct route *)SYSERR;
}

prt->rt_net = net;
prt->rt_mask = mask;
prt->rt_gw = gw;
prt->rt_metric = metric;
prt->rt_ifnum = ifnum;
prt->rt_ttl = ttl;
prt->rt_refcnt = 1; /* our caller */
prt->rt_usecnt = 0;
prt->rt_next = NULL;
return prt;
}

7.2 删除路由

/* rtdel.c – rtdel */

#include
#include
#include

/*————————————————————————
* rtdel – 删除给定网络的路由和掩码
*————————————————————————
*/
int
rtdel(IPaddr net, IPaddr mask)
{
struct route *prt, *prev;
int hv, i;

if (!Route.ri_valid) /* 未被初始化则返回出错*/
return SYSERR;
wait(Route.ri_mutex); /* 等待互斥量*/
if (Route.ri_default && /* 默认路由存在并且net参数与之相同,则删除默认路由*/
net == Route.ri_default->rt_net) {
RTFREE(Route.ri_default);
Route.ri_default = 0;
signal(Route.ri_mutex);
return OK;
}
hv = rthash(net); /* 计算哈希值*/

prev = NULL;
/* 查找匹配的路由*/
for (prt = rttable[hv]; prt; prt = prt->rt_next) {
if (net == prt->rt_net &&
mask == prt->rt_mask)
break;
prev = prt;
}
if (prt == NULL) { /* 没有找到匹配的路由*/
signal(Route.ri_mutex);
return SYSERR;
}
if (prev) /* prev不为空,说明匹配的路由在链表中间,并删除结点*/
prev->rt_next = prt->rt_next;
else /* 路由在链表开头*/
rttable[hv] = prt->rt_next;
RTFREE(prt); /* 释放结点*/
signal(Route.ri_mutex); /* 释放互斥量*/
return OK;
}

释放结点用的,没什么可说的,就是使用互斥量,调用一个宏
/* rtfree.c – rtfree */

#include
#include
#include

/*————————————————————————
* rtfree – remove one reference to a route
*————————————————————————
*/
int
rtfree(struct route *prt)
{
if (!Route.ri_valid)
return SYSERR;
wait(Route.ri_mutex);
RTFREE(prt);
signal(Route.ri_mutex);
return OK;
}

8. IP选项处理
没做什么工做,只是分析了一下IP首部中选面长度八位组的信息
/* ipdoopts.c – ipdoopts */

#include
#include
#include

/*————————————————————————
* ipdoopts – do gateway handling of IP options
*————————————————————————
*/
int
ipdoopts(struct netif *pni, struct ep *pep)
{
return OK; /* not implemented yet */
}

/* ipdstopts.c – ipdstopts */

#include
#include
#include

/*————————————————————————
* ipdstopts – do host handling of IP options
*————————————————————————
*/
int
ipdstopts(struct netif *pni, struct ep *pep)
{
struct ip *pip = (struct ip *)pep->ep_data;
u_char *popt, *popend;
int len;

if (IP_HLEN(pip) == IPMHLEN)
return OK;
popt = pip->ip_data;
popend = (u_char *)&pep->ep_data[IP_HLEN(pip)];

/* NOTE: options not implemented yet */

/* delete the options */
len = pip->ip_len-IP_HLEN(pip); /* data length */
if (len)
memcpy(pip->ip_data, &pep->ep_data[IP_HLEN(pip)], len);
pip->ip_len = IPMHLEN + len;
pip->ip_verlen = (pip->ip_verlen&0xf0) | IP_MINHLEN;
return OK;
}

问题:rtdel调用rtfree而不是使用宏 RTFREE,结果会怎么样? 会发生死锁

IP 总体结构 详解

1.中心环节

IP是整个协议软件的中心环节,不要把IP软件简单的分割成输入和输出两部分,因为输入和输出的界限很模糊.以下把重点放在网关上.

2. IP软件设计思想

 * 统一的输入队列以及统一的选路过程
 * 独立的IP进程
 * 本地主机接口

3. IP软件结构和数据报流程

4. IP软件结构和数据报流程

4.1 选择传入数据报的策略
挑选数据报并为其选择路由的IP程序段实现了一个重要策略:它决定了数据报来源的相对优先级.
公平分配优先权,例用策略为循环法(round-robin)

/* ipgetp.c – ipgetp */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

static    int    ifnext = NI_LOCAL;        //接口索引

/*————————————————————————
 * ipgetp  —  choose next IP input queue and extract a packet
 *————————————————————————
 */
struct ep *
ipgetp(int *pifnum)
{
    struct    ep    *pep;                        //以太帧结构体
    int        i;

    recvclr();                            /* 确保没有旧分组正在等待 */
    while (TRUE) {
        for (i=0; i < Net.nif; ++i, ++ifnext) {        //遍历每个接口
            if (ifnext >= Net.nif)            //等于或大于最大接口数
                ifnext = 0;
            if (nif[ifnext].ni_state == NIS_DOWN)    //此接口关闭
                continue;
            if (pep = NIGET(ifnext)) {            /*NIGET宏,提取并返回第一个数据*/
                *pifnum = ifnext;            //返回接口的序号
                return pep;                //返回以太帧的结构体指针
            }
        }
        ifnext = receive();    //如没有数据则阻塞,当有数据到达时,返回数据报到达的接口指向
    }
    /* can't reach here */
}

4.2 IP使用的常量定义

/* ip.h – IP_HLEN, IP_CLASS{A,B,C,D,E} */

/* 互联网协议(IP)的常量和数据报格式        */

#define    IP_ALEN    4        /* 以字节为单位的IP地址的长度(字节)    */
typedef    unsigned long IPaddr;    /* 互联网地址                */

#if    BYTE_ORDER == BIG_ENDIAN
#define    IP_CLASSA(x) (((x) & 0x80000000) == 0)        /* A类的IP */
#define    IP_CLASSB(x) (((x) & 0xc0000000) == 0x80000000)    /* B类的IP */
#define    IP_CLASSC(x) (((x) & 0xe0000000) == 0xc0000000)    /* C类的IP */
#define    IP_CLASSD(x) (((x) & 0xf0000000) == 0xe0000000)    /* D类的IP */
#define    IP_CLASSE(x) (((x) & 0xf8000000) == 0xf0000000)    /* E类的IP */
#else    /* BYTE_ORDER */
#define    IP_CLASSA(x)    (((x) & 0x80) == 0x00)    /* A类的IP地址    */
#define    IP_CLASSB(x)    (((x) & 0xc0) == 0x80)    /* B类的IP地址    */
#define    IP_CLASSC(x)    (((x) & 0xe0) == 0xc0)    /* C类的IP地址    */
#define    IP_CLASSD(x)    (((x) & 0xf0) == 0xe0)    /* D类的IP地址    */
#define    IP_CLASSE(x)    (((x) & 0xf8) == 0xf0)    /* E类的IP地址    */
#endif    /* BYTE_ORDER */

/* 部分分配协议编号 */

#define    IPT_ICMP    1    /* ICMP数据包协议类型    */
#define    IPT_IGMP    2    /* IGMP数据包协议类型    */
#define    IPT_TCP        6    /* TCP数据包协议类型    */
#define IPT_EGP        8    /* EGP数据包协议类型    */
#define    IPT_UDP        17    /* UDP数据包的协议类型    */
#define    IPT_OSPF    89    /* OSPF数据包协议类型    */

struct    ip    {
    u_char    ip_verlen;    /*  IP版本及头长度 (in longs)*/
    u_char    ip_tos;        /* 服务类型    */
    u_short    ip_len;        /* 包总长度(字节)    */
    short    ip_id;        /* 数据包的id                */
    short     ip_fragoff;    /* 碎片偏移(8字节的)    */
    u_char    ip_ttl;        /* 生存时间,在网关的跳    */
    u_char    ip_proto;    /*  IP协议(见IPT_ *)*/
    short    ip_cksum;    /* 头校验和             */
    IPaddr    ip_src;        /* 源IP地址            */
    IPaddr    ip_dst;        /* 目的地IP地址        */
    u_char    ip_data[1];    /* 可变长度的数据            */
};

#define    IP_VERSION    4    /* 当前版本值        */
#define    IP_MINHLEN    5    /* 最小的IP头长度 (in longs)    */
#define    IP_TTL        255    /* 生存时间初始值        */

#define    IP_MF        0x2000    /* 更多的碎片位            */
#define    IP_DF        0x4000    /* 不分片位            */
#define    IP_FRAGOFF    0x1fff    /* 碎片偏移 fragment offset mask*/
#define    IP_PREC        0xe0    /* 优先的服务 precedence portion of TOS*/

/* IP优先值 */

#define    IPP_NETCTL    0xe0    /* 网络控制            */
#define    IPP_INCTL    0xc0    /* Internet控制            */
#define    IPP_CRIT    0xa0    /* 关键 critical                */
#define    IPP_FLASHO    0x80    /* flash over-ride            */
#define    IPP_FLASH    0x60    /* flash                 */
#define    IPP_IMMED    0x40    /* immediate                */
#define    IPP_PRIO    0x20    /* 优先                */
#define    IPP_NORMAL    0x00    /* 正常                */

/*宏来计算一个数据报的报头的长度(字节)        */
#define    IP_HLEN(pip)    ((pip->ip_verlen & 0xf)<<2)
#define    IPMHLEN        20    /* 最小的IP头长度(字节)    */

/* IP选项 */
#define    IPO_COPY    0x80    /* 分片复制 copy on fragment mask        */
#define IPO_CLASS    0x60    /* 选项类 option class                */
#define    IPO_NUM        0x17    /* 选项编号option number        */

#define    IPO_EOOP    0x00    /* 选项结束            */
#define    IPO_NOP        0x01    /* 没有操作 no operation        */
#define    IPO_SEC        0x82    /* DoD security/compartmentalization    */
#define    IPO_LSRCRT    0x83    /* 松散源路由 loose source routing        */
#define    IPO_SSRCRT    0x89    /* 严格的源路由strict source routing        */
#define    IPO_RECRT    0x07    /* 记录路线                */
#define    IPO_STRID    0x88    /* 流编号                */
#define    IPO_TIME    0x44    /* 互联网时间戳            */

#define    IP_MAXLEN    BPMAXB-EP_HLEN    /*  IP数据报的最大长度    */

/*IP进程的信息 */

extern    PROCESS        ipproc();
#define    IPSTK        512    /* IP过程堆栈大小        */
#define    IPPRI        100    /* IP运行在高优先级        */
#define    IPNAM        "ip"    /* IP过程的名字            */
#define    IPARGC        0    /* count of args to IP             */

extern IPaddr    ip_maskall;    /* = 255.255.255.255            */
extern IPaddr    ip_anyaddr;    /* = 0.0.0.0                */
extern IPaddr    ip_loopback;    /* = 127.0.0.1                */

extern    int    ippid, gateway;

struct ip *iph2net(struct ip *), *ipnet2h(struct ip *);
unsigned short cksum(WORD *, unsigned);
int ipsend(IPaddr, struct ep *, unsigned, u_char, u_char, u_char);
int ipputp(unsigned, IPaddr, struct ep *);
Bool isbrc(IPaddr);

4.3 IP进程的结构 (IP进程允许被阻塞)

/* ipproc.c – ipproc */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct    ep    *ipgetp(int *);
struct    route    *rtget(IPaddr, Bool);

/*————————————————————————
 *  ipproc  –   处理一个从网络中来的IP数据包
 *————————————————————————
 */
PROCESS
ipproc(void)
{
    struct    ep    *pep;
    struct    ip    *pip;
    struct    route    *prt;
    Bool        nonlocal;
    int        ifnum;

    ippid = getpid();                        /* 得到进程的ID    */

    signal(Net.sema);                        /* 信号进行初始化*/

    while (TRUE) {
        pep = ipgetp(&ifnum);                /* 选取一个以太网数据报*/
        pip = (struct ip *)pep->ep_data;            /* 得到此报包含的更高级报地址*/

        if ((pip->ip_verlen>>4) != IP_VERSION) {        /* 判断是否为IP数据报*/
            IpInHdrErrors++;
            freebuf(pep);
            continue;
        }
        if (IP_CLASSE(pip->ip_dst)) {            /* 判断是否为E类地址*/
            IpInAddrErrors++;
            freebuf(pep);
            continue;
        }
        if (ifnum != NI_LOCAL) {                /* 此报不是本机产生的*/
            if (cksum((WORD *)pip, IP_HLEN(pip))) {    /* 计算检验和*/
                IpInHdrErrors++;
                freebuf(pep);
                continue;
            }
            ipnet2h(pip);
            pep->ep_order |= EPO_IP;
        }
        prt = rtget(pip->ip_dst, (ifnum == NI_LOCAL));    /* 选择路由*/

        if (prt == NULL) {                    /* 如果路由不存在*/
            if (gateway) {                /* 如网关存在发送ICMP不可达*/
                iph2net(pip);
                pep->ep_order &= ~EPO_IP;
                icmp(ICT_DESTUR, ICC_NETUR,
                        pip->ip_src, pep, 0);
            } else {
                IpOutNoRoutes++;
                freebuf(pep);
            }
            continue;
        }
        nonlocal = ifnum != NI_LOCAL && prt->rt_ifnum != NI_LOCAL; /* 不是本地地址*/
        if (!gateway && nonlocal) {                /* 不是网关也不是本地地址*/
            IpInAddrErrors++;
            freebuf(pep);
            rtfree(prt);
            continue;
        }
        if (nonlocal)
            IpForwDatagrams++;
        /* 如果我们发件人,填写在源IP */

        if (ifnum == NI_LOCAL) {                /* 如果网络接口为本地*/
            if (pip->ip_src == ip_anyaddr)       
                if (prt->rt_ifnum == NI_LOCAL)    /* 路由的接口为本地*/
                    pip->ip_src = pip->ip_dst;
                else
                    pip->ip_src =
                        nif[prt->rt_ifnum].ni_ip;
        } else if (–(pip->ip_ttl) == 0 &&        /* ttl 为零,且路由接口不为本地*/
                prt->rt_ifnum != NI_LOCAL) {
            IpInHdrErrors++;
            iph2net(pip);
            pep->ep_order &= ~EPO_IP;
            icmp(ICT_TIMEX, ICC_TIMEX, pip->ip_src, pep, 0);    /*发送ICMP超时报文*/
            rtfree(prt);
            continue;
        }
        ipdbc(ifnum, pep, prt);    /* 处理定向广播    */
        ipredirect(pep, ifnum, prt); /* 做重定向,如果需要的话*/
        if (prt->rt_metric != 0)
            ipputp(prt->rt_ifnum, prt->rt_gw, pep);
        else
            ipputp(prt->rt_ifnum, pip->ip_dst, pep);
        rtfree(prt);
    }
}

int    ippid, gateway, bsdbrc;

4.4 校验和的计算

/* cksum.c – cksum */

/*————————————————————————
 *  cksum  –  Return 16-bit ones complement of 16-bit ones complement sum
 *————————————————————————
 */
unsigned short
cksum(buf, nbytes)
unsigned short    *buf;
int        nbytes;
{
    unsigned long    sum;
    unsigned short    tmp;
    int        nwords;

    nwords = nbytes / 2;            /* 将字节转成字,也就是16个的个数 */
    for (sum=0; nwords>0; nwords–)    /* 将16位为单位做累加*/
        sum += *buf++;
    if (nbytes & 1) {            /* 如长度不是16位的倍数则补齐*/
        tmp = *(unsigned char *)buf;
        sum += tmp;
    }
    sum = (sum >> 16) + (sum & 0xffff);    /* 高位低位相加   */
    sum += (sum >> 16);            /* 上一步溢出时,将溢出位也加到sum中 */
    return (unsigned short)~sum;        /* 返回sum的反码*/
}

4.5 处理定向广播

主要做了两件事,复制一个副本,然后正常发送
/* ipdbc.c – ipdbc */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct    route *rtget(IPaddr, Bool);

/*————————————————————————
 * ipdbc – handle IP directed broadcast copying
 *————————————————————————
 */
void
ipdbc(unsigned ifnum, struct ep *pep, struct route *prt)
{
    struct    ip    *pip = (struct ip *)pep->ep_data;
    struct    ep    *pep2;
    struct    route    *prt2;
    int        len;

    if (prt->rt_ifnum != NI_LOCAL)
        return;            /* 不是我们        */
    if (!isbrc(pip->ip_dst))
        return;            /* 不是广播    */

    prt2 = rtget(pip->ip_dst, RTF_LOCAL);    /* 选择一个路由 */
    if (prt2 == NULL)
        return;
    if (prt2->rt_ifnum == ifnum) {    /* 没有针对接口        */
        rtfree(prt2);
        return;
    }

    /* 定向广播,建立一个副本 */

    /* len = ether header + IP packet */

    len = EP_HLEN + pip->ip_len;
    if (len > EP_MAXLEN)                    /* 为副本分配缓冲区*/
        pep2 = (struct ep *)getbuf(Net.lrgpool);
    else
        pep2 = (struct ep *)getbuf(Net.netpool);
    if (pep2 == (struct ep *)SYSERR) {
        rtfree(prt2);
        return;
    }
    memcpy(pep2, pep, len);                /* 复制副本*/
    /* 发送一份拷贝到网络 */

    ipputp(prt2->rt_ifnum, pip->ip_dst, pep2);
    rtfree(prt2);

    return;        /* continue; "pep" goes locally in IP    */
}

4.6 识别一个广播地址

/* isbrc.c – isbrc */

#include <conf.h>
#include <kernel.h>
#include <sleep.h>
#include <network.h>

/*————————————————————————
 *  isbrc  –  Is "dest" a broadcast address?
 *————————————————————————
 */
Bool
isbrc(IPaddr dest)
{
    int    inum;

    /* 所有的0和全1的是广播地址 */

    if (dest == ip_anyaddr || dest == ip_maskall)    /* 如果为全0 或 全1 则返回真*/
        return TRUE;

    /* 检查真正的广播地址和BSD风格的网和子网 */

    for (inum=0; inum < Net.nif; ++inum)
        if (dest == nif[inum].ni_brc ||
            dest == nif[inum].ni_nbrc ||
            dest == nif[inum].ni_subnet ||
            dest == nif[inum].ni_net)
            return TRUE;

    return FALSE;
}

5. IP首部中的字节顺序

/* iph2net.c – iph2net */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 *  iph2net –  iph2net – 转换成一个IP数据包头从主机到网络字节顺序
 *————————————————————————
 */
struct ip *
iph2net(struct ip *pip)
{
    /* 注:不包括IP选项    */

    pip->ip_len = hs2net(pip->ip_len);
    pip->ip_id = hs2net(pip->ip_id);
    pip->ip_fragoff = hs2net(pip->ip_fragoff);
    return pip;
}

/* ipnet2h.c – ipnet2h */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*————————————————————————
 *  ipnet2h – 转换成一个IP数据包头从网络到主机字节顺序
 *————————————————————————
 */
struct ip *
ipnet2h(struct ip *pip)
{
    /* 注:不包括IP选项    */

    pip->ip_len = net2hs(pip->ip_len);
    pip->ip_id = net2hs(pip->ip_id);
    pip->ip_fragoff = net2hs(pip->ip_fragoff);
    return pip;
}

6. 向IP发送数据报

6.1 发送本地生成的数据报

/* ipsend.c – ipsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

static ipackid = 1;            /* 静态变量 标识*/

/*————————————————————————
 *   ipsend – 发送一个IP数据报到指定地址
 *————————————————————————
 */
int
ipsend(IPaddr faddr, struct ep *pep, unsigned datalen,
    u_char proto, u_char ptos, u_char ttl)
{
    struct    ip *pip = (struct ip *) pep->ep_data;

    pep->ep_type = EPT_IP;                    /* 类型为网际协议*/
    pep->ep_order |= EPO_IP|EPO_NET;                /* 网络层 协议等级为1*/
    pip->ip_verlen = (IP_VERSION<<4) | IP_MINHLEN;        /* 协议版本 最小的IP头长度*/
    pip->ip_tos = ptos;                    /* 设置服务类型与优先级*/
    pip->ip_len = datalen+IP_HLEN(pip);            /* IP分组的长度*/
    pip->ip_id = ipackid++;                    /* 分组的标识*/
    pip->ip_fragoff = 0;                    /* 分片的偏移*/
    pip->ip_ttl = ttl;                    /* 生存时间*/
    pip->ip_proto = proto;                    /* 协议类型*/
    pip->ip_dst = faddr;                    /* 目标地址*/

    /*
     * 特殊情况的ICMP协议,因此源匹配目的地
     * on multi-homed hosts. 多穴主机??
     */
    if (pip->ip_proto != IPT_ICMP)                /* 不是ICMP分组*/
        pip->ip_src = ip_anyaddr;            /* IP的源地址为任意地址*/

    if (enq(nif[NI_LOCAL].ni_ipinq, pep, 0) < 0) {        /* 将分组放入本地主机接口队列中*/
        freebuf(pep);
        IpOutDiscards++;
    }
    send(ippid, NI_LOCAL);                    /* IP数据报阻塞并等待,发送报文给IP进程*/
    IpOutRequests++;
    return OK;
}
/* 特殊的IP地址 */

IPaddr    ip_anyaddr = 0;
#if    BYTE_ORDER == LITTLE_ENDIAN
IPaddr    ip_loopback = 0x0100007F;    /* 127.0.0.1 */
#else    /* BYTE_ORDER */
IPaddr    ip_loopback = 0x7F000001;    /* 127.0.0.1 */
#endif    /* BYTE_ORDER */

6.2 发送传入数据报

/* ip_in.c – ip_in */

#include <conf.h>
#include <kernel.h>
#include <network.h>
#include <proc.h>

/*————————————————————————
 *  ip_in – IP input function
 *————————————————————————
 */
int
ip_in(struct netif *pni, struct ep *pep)
{
    struct    ip    *pip = (struct ip *)pep->ep_data;

    IpInReceives++;
    if (enq(pni->ni_ipinq, pep, pip->ip_tos & IP_PREC) < 0) {
        IpInDiscards++;                        /* 如队列满则记录溢出差错并丢则报文*/
        freebuf(pep);
    }
    send(ippid, (pni-&nif[0]));                    /* 向IP进程发送消息*/
    return OK;
}

7. 表格的维护

/* slowtimer.c – slowtimer */

#include <conf.h>
#include <kernel.h>
#include <proc.h>
#include <sleep.h>
#include <date.h>
#include <network.h>

#define    STGRAN    1        /* 定时器粒度(延迟秒)    */

void arptimer(unsigned), ipftimer(unsigned), rttimer(unsigned);
#ifdef OSPF
void ospftimer(unsigned);
#endif /*OSPF*/

/*————————————————————————
 * slowtimer – 处理长期定期维护网络表
 *————————————————————————
 */
PROCESS
slowtimer(void)
{
    unsigned long    lasttime, now;    /* prev and current times (secs)*/
    int        delay;        /* 几秒钟内实际延迟    */

    signal(Net.sema);

    gettime(&lasttime);
    while (1) {
        sleep(STGRAN);        /* 延时*/
        gettime(&now);
        delay = now – lasttime;
        if (delay <= 0 || delay > 4*STGRAN)
            delay = STGRAN;    /* 时钟复位-likely clock reset */
        lasttime = now;
        arptimer(delay);
        ipftimer(delay);
        rttimer(delay);
#ifdef OSPF
        ospftimer(delay);
#endif /* OSPF */
    }
}