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