月度归档:2010年06月

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 */
    }
}
 

linux 使用 vi 编辑 16进制

用法很简单:

在vi的命令状态下

:%!xxd ——将当前文本转换为16进制格式。

:%!od ——将当前文本转换为16进制格式。

:%!xxd -c 12——将当前文本转换为16进制格式,并每行显示12个字节。

:%!xxd -r ——将当前文件转换回文本格式。

linux mplayer 字幕乱码 解决

1. 在~/.mplayer/config里加一行subcp=cp936(通常是这个编码)就行了,虽然指定了,但为什么字幕 变成下划线了呢,因为没有指定字体

2. 直接指定系统字体的名字,列如,在~/.mplayer/config加上这样一行 font='simhei'就可以了,这样一来,指定一个漂亮的字体就行了,例如simhei,只要把这个字体 文件拷贝到/usr/share/fonts/zh_CN/TrueType/,再运行一下fc-cache,然后指定font='SimHei'

3.运行mplayer 配置菜单里的subtitles & OSD 下面的encoding 选cp936

font里的font 指定字体文件就可以了

ARP 协议 代码 解释

1. ARP高速缓存的数据结构

/* arp.h – SHA, SPA, THA, TPA */

/* 互联网地址解析协议  (see RFCs 826, 920)    */

#define    AR_HARDWARE    1    /* 以太网硬件类型代码    */

/* Definitions of codes used in operation field of ARP packet */
    
#define    AR_REQUEST    1    /* ARP请求解决地址 ARP request to resolve address    */
#define    AR_REPLY    2    /* 回答解决的请求 reply to a resolve request    */

#define    RA_REQUEST    3    /* 反向ARP请求 reverse ARP request (RARP packets)*/
#define    RA_REPLY    4    /* 答复反向请求 reply to a reverse request (RARP ")*/

struct    arp    {
    u_short    ar_hwtype;    /* 硬件类型    */
    u_short    ar_prtype;    /* 协议类型    */
    u_char    ar_hwlen;    /* 硬件地址的长度    */
    u_char    ar_prlen;    /* 协议地址的长度    */
    u_short    ar_op;        /* ARP协议运作(见上述列表)    */
    u_char    ar_addrs[1];    /* 发送者和目标硬件及原地址    */
/*    char    ar_sha[???];     – 发件人的物理硬件地址    */
/*    char    ar_spa[???];     – 发送者的协议地址(IP地址)    */
/*    char    ar_tha[???];     – 目标物理硬件地址    */
/*    char    ar_tpa[???];     – 目标的协议地址(IP)    */
};

#define    SHA(p)    (&p->ar_addrs[0])                /*计算源物理地址*/
#define    SPA(p)    (&p->ar_addrs[p->ar_hwlen])            /*计算源协议地址*/
#define    THA(p)    (&p->ar_addrs[p->ar_hwlen + p->ar_prlen])    /*计算目标物理地址*/
#define    TPA(p)    (&p->ar_addrs[(p->ar_hwlen*2) + p->ar_prlen])    /*计算目标协议地址*/

#define    MAXHWALEN    EP_ALEN    /* 以太网地址最大长度            */
#define    MAXPRALEN    IP_ALEN    /* IP 地址最大长度            */

#define ARP_HLEN    8    /* ARP协议头长度            */

#define    ARP_TSIZE    50    /*  ARP缓存大小            */
#define    ARP_QSIZE    10    /*  ARP协议端口队列的大小        */

/* 缓存超时 cache timeouts */

#define ARP_TIMEOUT    600        /* 10 minutes                */
#define    ARP_INF        0x7fffffff    /* “无限”超时值 "infinite" timeout value    */
#define    ARP_RESEND    1    /* 如果没有在1秒内回复,重新发送            */
#define    ARP_MAXRETRY    4    /* 多少秒后放弃?? give up after ~30 seconds    */

struct    arpentry {        /* ARP缓存中的条目格式    */
    short    ae_state;    /* 本条目状态(见下文)    */
    short    ae_hwtype;    /* 硬件类型            */
    short    ae_prtype;    /* 协议类型            */
    char    ae_hwlen;    /* 硬件地址的长度        */
    char    ae_prlen;    /* 协议地址的长度        */
    struct netif *ae_pni;    /* 指向接口结构体的指针    */
    int    ae_queue;    /* 队列的数据包地址    */
    int    ae_attempts;    /* 重试,到目前为止 number of retries so far    */
    int    ae_ttl;        /* 生存时间                */
    u_char    ae_hwa[MAXHWALEN];    /* 硬件地址         */
    u_char    ae_pra[MAXPRALEN];    /* 协议地址         */
};

#define    AS_FREE        0    /* 表项空闲    */
#define    AS_PENDING    1    /* 表项使用中,但未绑定        */
#define    AS_RESOLVED    2    /* 表项正确绑定         */

/* RARP变量 */

extern int    rarppid;    /* 进程ID为RARP等待答复    */
extern int    rarpsem;    /* RARP服务获取信号量    */

/*  ARP协议的变量 */

extern struct    arpentry    arptable[ARP_TSIZE];

/*  ARP协议函数声明 */

int         arp_in(struct netif *, struct ep *);
struct arpentry *arpfind(u_char *, u_short, struct netif *);
struct arpentry *arpadd(struct netif *, struct arp *);


2. 搜索ARP高速缓存

/* arpfind.c – arpfind */

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

/*————————————————————————
 * arpfind – find an ARP entry given a protocol address and interface
 *————————————————————————
 */
struct arpentry *
arpfind(u_char *pra, u_short prtype, struct netif *pni)    /*参数:协议地址,协议类型,接口结构指针*/
{
    struct arpentry    *pae;                /*定义ARP缓存结构体指针*/
    int        i;

    for (i=0; i<ARP_TSIZE; ++i) {            /*遍历ARP的缓存*/
        pae = &arptable[i];            /*缓存数组*/
        if (pae->ae_state == AS_FREE)        /*缓存为空闲接找下一个*/
            continue;
        if (pae->ae_prtype == prtype &&    
            pae->ae_pni == pni &&
            BLKEQU(pae->ae_pra, pra, pae->ae_prlen))
            return pae;            /*如果缓存中的协议类型与参数相同,并且接口与协议地址全相同,则返回这个ARP条目的指针。BLKEQU的定义 #define    BLKEQU(b1, b2, len)    (!memcmp((b1), (b2), len))*/
    }
    return 0;
}
 


3. ARP请求广播分组

/* arpsend.c – arpsend */

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

/*————————————————————————
 * arpsend – broadcast an ARP request
 *    N.B. Assumes interrupts disabled
 *————————————————————————
 */
int
arpsend(struct arpentry *pae)                    /*指向ARP表项的指针*/
{
    struct    netif    *pni = pae->ae_pni;            /*指向接口结构指针*/
    struct    ep    *pep;                    /*贞结构指针*/
    struct    arp    *parp;                    /*ARP分组指针*/
    int        arplen;

    pep = (struct ep *) getbuf(Net.netpool);        /*为分组分配一个缓存区*/
    if ((int)pep == SYSERR)
        return SYSERR;
    memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen);/*目的地播为物理广播地址*/
    pep->ep_type = EPT_ARP;                    /*帧类型*/
    pep->ep_order = EPO_NET;                /*网络工作层*/
    
    /*填写ARP包*/
    parp = (struct arp *) pep->ep_data;            /*以太网帧中ARP的数据*/
    parp->ar_hwtype = hs2net(pae->ae_hwtype);        /*硬件类型*/        
    parp->ar_prtype = hs2net(pae->ae_prtype);        /*协议类型*/
    parp->ar_hwlen = pae->ae_hwlen;                /*硬件地址长度*/
    parp->ar_prlen = pae->ae_prlen;                /*协议地址长度*/
    parp->ar_op = hs2net(AR_REQUEST);            /*操作字段,请求*/
    memcpy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen);    /*发送方硬件地址*/
    memcpy(SPA(parp), &pni->ni_ip, pae->ae_prlen);        /*发送方协议地址*/
    memset(THA(parp), 0, pae->ae_hwlen);            /*目标硬件地址清空*/
    memcpy(TPA(parp), pae->ae_pra, pae->ae_prlen);        /*目标协议地址*/
    arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen);/*ARP的总长度*/
    write(pni->ni_dev, pep, EP_HLEN+arplen);        /*发送分组*/
    return OK;
}


4. ARP输出过程

/* netwrite.c – netwrite */

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

#include <ospf.h>

struct    arpentry    *arpalloc();
struct    arpentry     *arpfind(u_char *, u_short, struct netif *);
int    arpsend(struct arpentry *);
int    local_out(struct ep *);        //用于将目的是本地机的数据包通过loopback 发送到指定本机端口

/*#define    DEBUG */

/*————————————————————————
 * netwrite – write a packet on an interface, using ARP if needed
 *————————————————————————
 */
int
netwrite(struct netif *pni, struct ep *pep, unsigned len)
{
    struct    arpentry     *pae;            //ARP项目指针
    STATWORD        ps;            //状态字

    if (pni->ni_state != NIS_UP) {            
        freebuf(pep);
        return SYSERR;
    }                        //接口未打开则退出
    pep->ep_len = len;                //帧长度
#ifdef    DEBUG                        
if (pni != &nif[NI_LOCAL])
{
struct ip *pip = (struct ip *)pep->ep_data;
    if (pip->ip_proto == IPT_OSPF) {
        struct ospf *po = (struct ospf *)pip->ip_data;
/*        if (po->ospf_type != T_HELLO) { */
{
            kprintf("netwrite(pep %X, len %d)n", pep, len);
            pdump(pep);
        }
    }
}
#endif    /* DEBUG */
    if (pni == &nif[NI_LOCAL])        //如果是本地接口,用local_out 发送
        return local_out(pep);
    else if (isbrc(pep->ep_nexthop)) {    /*否则,首先检查下一跳是否是广播地址*/
        memcpy(pep->ep_dst, pni->ni_hwb.ha_addr, EP_ALEN);//填写目的物理地址为广播物理地址
        write(pni->ni_dev, pep, len);    //发送
        return OK;
    }
    /* 否则,该协议地址查找… */

    disable(ps);    /*如果下一跳地址不是广播地址,则先在arp 缓存表中查找是否有绑定的物理地址,有则直接发送,没有则先做arp 地址绑定。*/
    pae = arpfind((u_char *)&pep->ep_nexthop, pep->ep_type, pni);
    if (pae && pae->ae_state == AS_RESOLVED) {
        memcpy(pep->ep_dst, pae->ae_hwa, pae->ae_hwlen);
        restore(ps);
        return write(pni->ni_dev, pep, len);
    }
    if (IP_CLASSD(pep->ep_nexthop)) {    /*下一跳地址为d 类地址则直接返回*/
        restore(ps);
        return SYSERR;
    }
    if (pae == 0) {                //没有绑定,做arp 解析
        pae = arpalloc();        //分配空间
        pae->ae_hwtype = AR_HARDWARE;    //硬件类型
        pae->ae_prtype = EPT_IP;    //协议类型
        pae->ae_hwlen = EP_ALEN;    //硬件地址长度
        pae->ae_prlen = IP_ALEN;    //协议地址长度
        pae->ae_pni = pni;        //接口
        pae->ae_queue = EMPTY;        //队列地址
        memcpy(pae->ae_pra, &pep->ep_nexthop, pae->ae_prlen);    //协议地址
        pae->ae_attempts = 0;        //重试
        pae->ae_ttl = ARP_RESEND;    //生存时间
        arpsend(pae);            //发送
    }
    /*将等待ARP绑定的数据包存放在和ARP表项相关的队列中,如果队列为空,则分配一个。*/
    if (pae->ae_queue == EMPTY)    
        pae->ae_queue = newq(ARP_QSIZE, QF_NOWAIT);    //如没有队列,建立一个新队列
    if (enq(pae->ae_queue, pep, 0) < 0)    //将分组放入队列中,
        freebuf(pep);
    restore(ps);
    return OK;    //如果队列满,则失丢这个分组
}


5. ARP的输入处理

/* arpadd.c – arpadd */

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

struct arpentry *arpalloc(void);

/*————————————————————————
 * arpadd – Add a RESOLVED entry to the ARP cache
 *     N.B. Assumes interrupts disabled
 *————————————————————————
 */
struct    arpentry *
arpadd(struct netif *pni, struct arp *parp)
{
    struct    arpentry    *pae;            //ARP缓存表项指针

    pae = arpalloc();                //为指针分配内存空间
    //从收到的ARP包中填写到缓存中
    pae->ae_hwtype = parp->ar_hwtype;        //硬件类型        
    pae->ae_prtype = parp->ar_prtype;        //协议类型
    pae->ae_hwlen = parp->ar_hwlen;            //硬件地址长度
    pae->ae_prlen = parp->ar_prlen;            //协议地址长度
    pae->ae_pni = pni;                //接口
    pae->ae_queue = EMPTY;                //队列地址
    memcpy(pae->ae_hwa, SHA(parp), parp->ar_hwlen);    //源物理地址
    memcpy(pae->ae_pra, SPA(parp), parp->ar_prlen);    //源协议地址
    pae->ae_ttl = ARP_TIMEOUT;            //生存时间
    pae->ae_state = AS_RESOLVED;            //表项的状态
    return pae;
}


6. 发送等待发送的分组

/* arpqsend.c – arpqsend */

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

int netwrite(struct netif *, struct ep *, unsigned);

/*————————————————————————
 * arpqsend – write packets queued waiting for an ARP resolution
 *————————————————————————
 */
void
arpqsend(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    netif    *pni;

    if (pae->ae_queue == EMPTY)        //待发送队列为空则返回
        return;

    pni = pae->ae_pni;            //接口
    while (pep = (struct ep *)deq(pae->ae_queue))    //遍历队列并发送,为空则释放队列
        netwrite(pni, pep, pep->ep_len);
    freeq(pae->ae_queue);
    pae->ae_queue = EMPTY;
}


7. ARP输入过程

/* arp_in.c – arp_in */

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

void arpqsend(struct arpentry *);

/*————————————————————————
 *  arp_in  –  handle ARP packet coming in from Ethernet network
 *    N.B. – Called by ni_in– SHOULD NOT BLOCK
 *————————————————————————
 */
int
arp_in(struct netif *pni, struct ep *pep)
{
    struct    arp        *parp = (struct arp *)pep->ep_data;
    struct    arpentry    *pae;
    int            arplen;
    /*从网络字节顺序转换成主机字节顺序*/
    parp->ar_hwtype = net2hs(parp->ar_hwtype);
    parp->ar_prtype = net2hs(parp->ar_prtype);
    parp->ar_op = net2hs(parp->ar_op);
    //如果硬件类型或协议类型不匹配,则丢充此分组并返回
    if (parp->ar_hwtype != pni->ni_hwtype ||
        parp->ar_prtype != EPT_IP) {
        freebuf(pep);
        return OK;
    }
    //如果ARP表项中有此IP,就更新之
    if (pae = arpfind(SPA(parp), parp->ar_prtype, pni)) {
        memcpy(pae->ae_hwa, SHA(parp), pae->ae_hwlen);
        pae->ae_ttl = ARP_TIMEOUT;
    }
    //如果此分组不是发往此接口的,则丢弃之
    if (memcmp(TPA(parp), &pni->ni_ip, IP_ALEN)) {
        freebuf(pep);
        return OK;
    }
    //如果是发往本接口并没有此表项,则加入一个新表项
    if (pae == 0)
        pae = arpadd(pni, parp);
    //如果表项状态为正在绑定中,则发送队列中的分组
    if (pae->ae_state == AS_PENDING) {
        pae->ae_state = AS_RESOLVED;
        arpqsend(pae);
    }
    //如果收到的分组操作类型为请求ARP,则填写ARP包并发送,否则丢充分组
    if (parp->ar_op == AR_REQUEST) {
        parp->ar_op = AR_REPLY;
        memcpy(TPA(parp), SPA(parp), parp->ar_prlen);
        memcpy(THA(parp), SHA(parp), parp->ar_hwlen);
        memcpy(pep->ep_dst, THA(parp), EP_ALEN);
        memcpy(SHA(parp), pni->ni_hwa.ha_addr,
            pni->ni_hwa.ha_len);
        memcpy(SPA(parp), &pni->ni_ip, IP_ALEN);

        parp->ar_hwtype = hs2net(parp->ar_hwtype);
        parp->ar_prtype = hs2net(parp->ar_prtype);
        parp->ar_op = hs2net(parp->ar_op);

        arplen = ARP_HLEN + 2*(parp->ar_prlen + parp->ar_hwlen);

        write(pni->ni_dev, pep, arplen);
    } else
        freebuf(pep);
    return OK;
}


8. ARP高速缓存的管理

/* arpalloc.c – arpalloc */

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

void arpdq(struct arpentry *);

/*————————————————————————
 * arpalloc – allocate an entry in the ARP table
 *    N.B. Assumes interrupts DISABLED
 *————————————————————————
 */
struct arpentry *arpalloc()
{
    static    int    aenext = 0;    /*静态变量*/
    struct    arpentry *pae;
    int    i;
    //找空表项
    for (i=0; i<ARP_TSIZE; ++i) {
        if (arptable[aenext].ae_state == AS_FREE)
            break;
        aenext = (aenext + 1) % ARP_TSIZE;    //实现静态变量循环计数
    }
    pae = & arptable[aenext];
    aenext = (aenext + 1) % ARP_TSIZE;
    //如没有空表项,则使用这个当前的表项,判断为使用中但未绑定,并且有队列,如果有则丢弃队列中的分组
    if (pae->ae_state == AS_PENDING && pae->ae_queue >= 0)
        arpdq(pae);
    pae->ae_state = AS_PENDING;
    return pae;
}


9. 高速缓存的定期维护管理

/* arptimer.c – arptimer */

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

void    arpdq(struct arpentry *);
int    arpsend(struct arpentry *);

/*————————————————————————
 * arptimer – Iterate through ARP cache, aging (possibly removing) entries
 *————————————————————————
 *    gran    – time since last iteration
 */
void
arptimer(int gran)
{
    struct arpentry *pae;
    STATWORD    ps;
    int        i;

    disable(ps);    /* 互斥量 */

    for (i=0; i<ARP_TSIZE; ++i) {
        if ((pae = &arptable[i])->ae_state == AS_FREE)
            continue;                /*空闲的条目*/
        if (pae->ae_ttl == ARP_INF)
            continue;                 /*不超时永久条目*/
        if ((pae->ae_ttl -= gran) <= 0)            /*寿命为0或负*/
            if (pae->ae_state == AS_RESOLVED)    /*条目绑定中但未正确*/
                pae->ae_state = AS_FREE;    /*仅改变状态为空闲*/
            else if (++pae->ae_attempts > ARP_MAXRETRY) {    /*超过最大广播次数*/
                pae->ae_state = AS_FREE;
                arpdq(pae);            /*清空队列*/
            } else {
                pae->ae_ttl = ARP_RESEND;    /*等于重发时间*/    
                arpsend(pae);            /*重新广播分组*/
            }
    }
    restore(ps);
}


10. 释放队列中的分组

/* arpdq.c – arpdq */

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

/*————————————————————————
 * arpdq – destroy an arp queue that has expired
 *————————————————————————
 */
void
arpdq(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    ip    *pip;

    if (pae->ae_queue < 0)        /* 队列为空,直接返回 */
        return;

    while (pep = (struct ep *)deq(pae->ae_queue)) {
        if (gateway && pae->ae_prtype == EPT_IP) {/*本机是网关并且数据报为IP数据报*/
            pip = (struct ip *)pep->ep_data;    //生成ICMP"目的站不可达"信息
            icmp(ICT_DESTUR, ICC_HOSTUR, pip->ip_src, pep, 0);
        } else
            freebuf(pep);            //释放帧
    }
    freeq(pae->ae_queue);                //释放队列
    pae->ae_queue = EMPTY;
}


11. ARP初始化

/* arpinit.c – arpinit */

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

/*————————————————————————
 *  arpinit  –  initialize data structures for ARP processing
 *————————————————————————
 */
void arpinit()
{
    int    i;

    rarpsem = screate(1);            /*创建一个与RARP一起使用的互斥型信号量*/
    rarppid = BADPID;            /*RARP进程号初始为-1*/

    for (i=0; i<ARP_TSIZE; ++i)
        arptable[i].ae_state = AS_FREE;    /*将状态全部置为AS_FREE*/
}

int    rarpsem;
int    rarppid;

struct    arpentry    arptable[ARP_TSIZE];    /*定义ARP高速缓存*/

解决 godaddy 空间 无法 自动发送 wordpress 评论 通知 方法

安装
WP-Mail-SMTP插件

安完后点插件->WP-Mail-SMTP->settings

From
From Email:     写上你发出邮件到邮箱时显示的发件人的地址

From Name:   发出邮件到邮箱时显示的发件人的名字

Mailer: 选第一个Send all WordPress emails via SMTP.

SMTP Host:relay-hosting.secureserver.net

SMTP Port: 25

Encryption:选第一个No encryption.

Authentication:选第一个No: Do not use SMTP authentication.

Username:为空
Password: 为空

点击 update options保存

然后在send test email to:
写上你要发送到哪里,再点 send test 看看发送成功了没

注意:我测试时用qq邮箱不行,用gmail的没问题,其它的邮箱自己试试吧

Virtualbox 使用物理硬盘 (ACL) for linux

物理硬盘在linux中在/dev目录下 ,我的是阵列,是/dev/dm-1 如果是硬盘就是/dev/sda 或sdb之类的

这些文件是root用户,disk组。vbox并不能直接访问,可以chmod o+rw /dev/dm-1,但是这样并不安全。

我使用ACL访问控制列表

先su – 进入root

setfacl -m u:ssj:rw /dev/dm-1

建立映射

VBoxManage internalcommands createrawvmdk -filename /home/ssj/.VirtualBox/HardDisks/phard.vmdk -rawdisk /dev/dm-1 -register

由于是root ,正常的用户没法用,所以还是先

cd /home/ssj/.VirtualBox/HardDisks/

setfacl -m u:ssj:rw phard.vmdk

getfacl phard.vmdk

再在vbox里把phard.vmdk加入就可以了。

重启后发现/dev/dm-1的权限又变回去了,不能访问了,我是把ssj这个用户加入到了disk这个组里

vi /etc/group

disk:x:6:root,ssj

网络接口层 使用的数据结构

1. 接口数据结构

/* netif.h – NIGET */

#define    NI_MAXHWA 14            /* 任何硬件最大尺寸 max size of any hardware*/
                    /* (物理)网络地址 */
struct    hwa    {            /* *硬件地址    */
    int        ha_len;        /* 地址长度    */
    unsigned char    ha_addr[NI_MAXHWA];     /* 地址    */
};

#define    NI_INQSZ    30        /* 接口输入队列大小    */
#define    NETNLEN        30        /* 网络名称长度    */

#define    NI_LOCAL    0        /* 本地接口序号index of local interface    */
#define    NI_PRIMARY    1        /* 主要接口序号index of primary interface    */

#define    NI_MADD        0        /* 添加多播add multicast (ni_mcast)    */
#define    NI_MDEL        1        /* 删除多播 multicast (ni_mcast)    */

/* 接口状态 */

#define    NIS_UP        0x1
#define    NIS_DOWN    0x2
#define    NIS_TESTING    0x3

/* 网络接口结构 (one per interface)    */

struct    netif {                /* 网络接口信息    */
    char        ni_name[NETNLEN]; /* 接口的域名    */
    char        ni_state;    /* 接口的状态 取上边的三个值    */
    IPaddr        ni_ip;        /* 接口的IP地址*/
    IPaddr        ni_net;        /* 接口的网络IP地址        */
    IPaddr        ni_subnet;    /* 子网的IP地址    */
    IPaddr        ni_mask;    /* 接口的子网掩码    */
    IPaddr        ni_brc;        /* IP广播地址        */
    IPaddr        ni_nbrc;    /* IP的网络广播地址    */
    unsigned int    ni_mtu;        /* 最大传输单元(字节)    */
    unsigned int    ni_hwtype;    /* 硬件类型 (for ARP)    */
    struct    hwa    ni_hwa;        /* 接口的硬件地址*/
    struct    hwa    ni_hwb;        /* 硬件广播地址    */
                    /* 多播添加/删除函数    */
    int        (*ni_mcast)(int op,int dev,Eaddr hwa,IPaddr ipa);
    Bool        ni_ivalid;    /* 接口的IP地址是否有效        */
    Bool        ni_nvalid;    /* 接口的域名是否有效        */
    Bool        ni_svalid;    /* 子网的IP地址是否有效        */
    int        ni_dev;        /* 设备描述符    */
    int        ni_ipinq;    /*  IP输入队列        */
    int        ni_outq;    /* (设备)的输出队列    */
    /* 接口信息管理库- MIB */
    char        *ni_descr;    /* 硬件的文字描述 */
    int        ni_mtype;    /* MIB类型        */
    long        ni_speed;    /* 速率 bit/s        */
    char        ni_admstate;    /* 管理状态 (NIS_*)*/
    long        ni_lastchange;    /* 最后一个状态变化 (1/100 sec)*/
    long        ni_ioctets;    /* # 收到的字节        */
    long        ni_iucast;    /* # 收到的单播字节    */
    long        ni_inucast;    /* # 收到的非单播字节    */
    long        ni_idiscard;    /* # 丢弃 – 输出队列满*/
    long        ni_ierrors;    /* # 输入包错误    */
    long        ni_iunkproto;    /* # in packets for unk. protos */
    long        ni_ooctets;    /* # 发送的字节        */
    long        ni_oucast;    /* # 发送的单播字节        */
    long        ni_onucast;    /* # 发送的非单播字节    */
    long        ni_odiscard;    /* # 输出的数据包被丢弃    */
    long        ni_oerrors;    /* # 发送的包错误    */
    long        ni_oqlen;    /* 输出队列长度        */
    long        ni_maxreasm;    /* 可以重新组装的最大数据包的长度    */
};

#define    NIGET(ifn)    ((struct ep *)deq(nif[ifn].ni_ipinq))

#define    NIF    Neth+Noth+1        /* # of interfaces, +1 for local*/

extern struct netif    nif[];

int netwrite(struct netif *, struct ep *, unsigned);
 


2. 以太网的基本定义

/* ether.h */

/* 以太网的定义和常量 */

#define    EP_MAXMULTI      10    /* 多播地址表的大小        */

#define    EP_MINLEN      60    /* 最小数据包长度        */
#define    EP_DLEN        1500    /* 数据字段长度        */
#define    EP_HLEN          24    /* 头长度    */
#define    EP_CRC           4    /* 效检位长度 ether CRC (trailer)    */
#define    EP_MAXLEN    EP_HLEN+EP_DLEN
#define    EP_ALEN    6        /* 物理地址的字节数    */
typedef    unsigned char    Eaddr[EP_ALEN]; /* 物理地址    */
#define    EP_RETRY    3    /* number of times to retry xmit errors    */
#define    EP_BRC    "377377377377377377"/* 以太网广播地址    */
#define EP_RTO 300        /* time out in seconds for reads    */

#define EP_NUMRCV 16        /* number LANCE recv buffers (power 2)    */
#define EP_NUMRCVL2 4        /* log2 of the number of buffers    */

#define    EPT_LOOP    0x0060        /* type: Loopback 回环        */
#define    EPT_ECHO    0x0200        /* type: Echo    应答        */
#define    EPT_PUP        0x0400        /* type: Xerox PUP        */
#define    EPT_IP        0x0800        /* type: Internet Protocol    */
#define    EPT_ARP        0x0806        /* type: ARP            */
#define    EPT_RARP    0x8035        /* type: Reverse ARP        */

struct    eh {            /* 以太网报头            */
    Eaddr    eh_dst;        /* 目标主机地址        */
    Eaddr    eh_src;        /* 源主机地址            */
    unsigned short    eh_type;/* 以太网数据包类型(见下文)    */
};

struct    ep    {        /* 结构完整的以太网数据包*/
    u_long    ep_nexthop;    /* niput() uses this            */
    short    ep_ifn;        /* 原接口序号originating interface number*/
    short    ep_len;        /* 数据包的长度            */
    short    ep_order;    /* 字节顺序 (for debugging)    */
    struct    eh ep_eh;    /* 以太网报头            */
    char    ep_data[EP_DLEN];    /* 数据        */
};

/* 数据包字节顺序的常量(设置方法主机顺序) */

#define    EPO_NET        1    /* 网络层 network layer        */
#define    EPO_IP        2    /* level 1 protocols…        */
#define    EPO_ARP        2
#define    EPO_RARP    2
#define    EPO_ICMP    4    /* level 2 protocols        */
#define    EPO_IGMP    4
#define    EPO_TCP        4
#define    EPO_UDP        4
#define    EPO_OSPF    4
#define    EPO_DNS        5    /* 应用协议 application protocols*/

/* these allow us to pretend it's all one big happy structure */

#define    ep_dst    ep_eh.eh_dst
#define    ep_src    ep_eh.eh_src
#define    ep_type    ep_eh.eh_type

#if    Noth > 0
/* this is for Othernet simulation */

struct    otblk {
    Bool    ot_valid;    /* these entries are valid?        */
    Eaddr    ot_paddr;    /* the Othernet physical address    */
    Eaddr    ot_baddr;    /* the Othernet broadcast address    */
    int    ot_rpid;    /* id of process reading from othernet    */
    int    ot_rsem;    /* mutex for reading from the othernet    */
    int    ot_pdev;    /* Physical device devtab index        */
    int    ot_intf;    /* the associate interface        */
    char    *ot_descr;    /* text description of the device    */
};
#endif    /* Noth */

#define    ETOUTQSZ    30

/* 以太网控制模块的描述 */

struct    etblk    {
    int    etxpending;    /* 号的数据包等待输出number of packets pending output*/
    int    etrpending;    /* 正在等待已接收1 => a receive is pending already*/
    int    etwtry;        /* 重试num. of times to retry xmit errors    */
    char    *etwbuf;    /* 当前发送缓冲区的指针    */
    short    etnextbuf;    /* 检查缓冲区循环for checking buffers round robin    */
#if    Noth > 0
    struct    otblk    *etoth[Noth];    /* eaddr to oaddr translations    */
#endif    /* Noth */
};

/* 以太网功能的代码 */

#define    EPC_PROMON    1        /* 打开混杂模式    */
#define    EPC_PROMOFF    2        /* 关闭混杂模式    */
#define    EPC_MADD    3        /* 添加多播地址    */
#define    EPC_MDEL    4        /* 删除多播地址    */

struct utdev {
    unsigned int     ud_iomem;
    unsigned int     ud_iosize;
    struct devsw    *ud_pdev;
    Eaddr         ud_paddr;
    Eaddr         ud_bcast;
    char        *ud_descr;
    int         ud_outq;
    int         ud_ifnum;
    int         ud_nextbuf;
    int         ud_xpending;
    int         ud_xmaddr;    /* 发送基地缓冲区transmit base buf #    */
    int         ud_wretry;
    int         ud_rmin;    /* 接收环形缓冲区基地址receive ring buffer base addr*/
    int         ud_rmax;    /* 环形缓冲区最大接收地址receive ring buffer max addr    */
};

struct etdev {
    unsigned int     ed_iomem;
    unsigned int     ed_iosize;
    struct devsw    *ed_pdev;
    Eaddr         ed_paddr;
    Eaddr         ed_bcast;
    char        *ed_descr;
    int         ed_outq;
    int         ed_ifnum;
    int         ed_wretry;
    struct scb    *ed_scb;
    struct cbl    *ed_cbl;
    struct rfd    *ed_rfd;    /* first in RFD ring        */
    struct rfd    *ed_rfdend;    /* last in RFDring        */
    struct rbd    *ed_rbd;    /* first in RBD ring        */
    struct rbd    *ed_rbdend;    /* last in RBD ring        */
    struct tbd    *ed_tbd;    /* transmit buffer descriptor    */
    unsigned char    *ed_xmbuf;    /* transmit buffer base        */
    unsigned char     ed_irq;    /* SIRQ reg value        */
    int         ed_mcc;    /* # valid mca's        */
    Eaddr         ed_mca[EP_MAXMULTI]; /* multicast addr. table    */
/* XXX */
    int        ed_xpending;
};

extern struct utdev    ue[];
extern struct etdev    ee[];
 


3. 传入分组的多路分解

/* ni_in.c – ni_in */

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

#include <ospf.h>

int arp_in(struct netif *, struct ep *);
int rarp_in(struct netif *, struct ep *);
int ip_in(struct netif *, struct ep *);

/*————————————————————————
 *  ni_in – network interface input function 网络接口输入功能
 *————————————————————————
 */
int
ni_in(struct netif *pni, struct ep *pep, unsigned len)
{
    int    rv;

    pep->ep_ifn = pni – &nif[0];    /* 记录原接口序号 # */

    pni->ni_ioctets += len;        /*收到的字节累加*/
    if (!memcmp(pni->ni_hwa.ha_addr, pep->ep_dst, EP_ALEN))
        pni->ni_iucast++; /*比较帧的目的地址与接口的物理地址,相同则单播字节累加*/
    else
        pni->ni_inucast++;    /*非单播字节累加*/
    switch (pep->ep_type) {        /*分类*/
    case EPT_ARP:    rv = arp_in(pni, pep);    break;
    case EPT_RARP:    rv = rarp_in(pni, pep);    break;
    case EPT_IP:    rv = ip_in(pni, pep);    break;
    default:
        pni->ni_iunkproto++;
        freebuf(pep);
        rv = OK;
    }
    return rv;
}

extern    struct    etblk    eth[];