Chinaunix首页 | 论坛 | 博客
  • 博客访问: 198802
  • 博文数量: 29
  • 博客积分: 1280
  • 博客等级: 中尉
  • 技术积分: 320
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-22 16:23
文章分类

全部博文(29)

文章存档

2009年(3)

2008年(1)

2007年(1)

2006年(3)

2005年(21)

我的朋友

分类: BSD

2005-10-17 14:26:35

又发个为完成的.关于OPENBSD的CARP(通用地址冗余协议)

CARP协议详解

                     --CARP协议原理及结构

/*作者:xie_minix*/

 

CARP ---通用地址冗余协议

源代码:(OpenBSD系统) src/sys/netinet/ip_carp.h, Revision 1.8

在核心配置文件/sys/arch/i386/conf/GENERIC中.定义为:

pseudo-device carp  [count]

其中count 为支持虚拟设备carp的数量

描述:

        carp接口为一虚拟设备.(注:虚拟设备即在机器中不真实存在).此种设备一般是使用通用

接口的克隆技术来生成.比如在carp程序中的挂接设备函数(一般设备的挂接都是使用"设备名

+attach")carpattch只是简单调用通用接口文件(if.c)中的通用设备克隆挂接函数

if_clone_attach(源代码:753行).实际上是在所有的使用克隆产生的设备列表中插入该carp

设备到表头(全局变量if_cloners是所有克隆设备的链表头.源代码:if.c第135行).

    CARP协议是在IP之上的一种协议.请注意carp和CARP的不同.即carp表示的是一种虚拟设备

.此设备提供对CARP协议的支持.关于CARP协议将在以下介绍.先来看看carp的作用.carp接口允

许本地网络的(注意是同一网段,不能跨越路由器)多个机器来共享一个(一组)IP地址.实际上的

结果就是当一台该IP地址的主机在出现意外事故的情况下不能工作.其它的机器能够立刻自动

的接替其工作.这一点对于防火墙系统来说提供冗余功能是非常不错的.目前的具有冗余功能的

防火墙系统也只有OpenBSD系统.当然我在从事ARP研究时曾经提出过类似问题,即如何在核心内

实现IP冒充技术.但只能是单向实现欺骗.对carp进行一些设置后它还能提供负载均衡功能.关于

这些文章详见Ryan McBride的文章.

 

使用方法:

    要使用carp设备必须首先在编译内核时加入对carp的支持.

    在/sys/arch/i386/conf/GENERIC (或你自己定义的核心配置文件)中加入:

    pseudo-device carp 16 (注意:在Man手册中有参数,即在carp后可以接参数,但在最新的GENERIC中没有说明接参数)

    编译核心后使用ifconfig carp0 create 来建立carp第0号设备.

  使用:ifconfig carp0 vhid 1 pass mekmitasdigoat 192.168.1.10  255.255.255.0来设置本机的第一个carp设备.

  意思是carp第0号设备(即第一个)的主机号为1,pass后接的是要用SHA1加密的通讯字符(当然可以自己随便取,不过只

  是前20个字符有用,下面我会有说明).IP地址是设置的carp设备的IP地址.

  说明:

  实际上任何的ifconfig都将调用欲设置的设备的源代码部分的ioctl函数(即该设备名+"_ioctl",在此例中是carp_ioctl).

  我们的参数1,mekmitasdigoat等都将放到一个叫carpreq结构中.在源代码的1594行有申明:struct carpreq carpr;

  通过copyin(ifr->ifr_data, &carpr, sizeof carpr) (源代码1687行) 从用户区把参拷贝到核心区的carpreq结构的实例

  carpr中.然后设置该设备的硬件地址.我们看看源代码的1718行:

  sc->sc_vhid = carpr.carpr_vhid;
  sc->sc_ac.ac_enaddr[0] = 0;
  sc->sc_ac.ac_enaddr[1] = 0;
  sc->sc_ac.ac_enaddr[2] = 0x5e;
  sc->sc_ac.ac_enaddr[3] = 0;
  sc->sc_ac.ac_enaddr[4] = 1;
  sc->sc_ac.ac_enaddr[5] = sc->sc_vhid;

  以上的ac_enaddr的前五位地址说明CARP协议使用的是多播地址.硬件地址使用的最后一位是参数中的主机号,也就是说.

  ifconfig中的主机号是用于设置carp的地址用的.

  参数mekmitasdigoat在源代码的第1740行进行了处理.bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key));

  即把参数(结构carpreq的成员carpr_key)放到了结构carp_softc的成员sc_key中.结构carp_softc是接口设备的专用

  数据结构,每个接口都有相应的该结构(详见xie_minix写的"以太网通用驱动源代码详解").其成员将在下一篇的"carp的

  源代码实现"中会详细介绍.我们现在看看carp_softc的成员sc_key的长度.源代码127行:unsigned char sc_key[CARP_KEY_LEN];

  其中CARP_KEY_LEN在本文(即carp.h头文件)有定义.长度为20.这就是为什么参数最大长度为20个字符的原因.以上的设置只是

 对备用机的设置.对主用机的设置用以下语法:

 ifconfig carp0 create

 ifconfig carp0 vhid 1 advskew 100 pass mekmitasdigoat 192.168.1.10 255.255.255.0

 当然这些语句是在另一台机器上执行的.唯一的区别是多了参数advskew 100,该参数是用来设定主用机发送广告包(CARP)的频率

 

 

CARP协议结构框图:
           

                           位长           |0------45------7|8------------1516------------2324------------31|

版本类型虚拟的主机IDCARP广告时间微秒级COUNTER+HMAC的32位长度即第3-9行的行数量
保留CARP广告时间秒级校验和
64位计数器的高32位
64位计数器的低32位
本机的MAC地址HASH值(第一部分)
本机的MAC地址HASH值(第二部分)
本机的MAC地址HASH值(第三部分)
本机的MAC地址HASH值(第四部分)
本机的MAC地址HASH值(第五部分)
                      上图的具体解释在下面的结构说明中.
1.1           mcbride     56: struct carp_header {  /*CARP协议头部,实际上该协议只有头部*/
                          57: #if BYTE_ORDER == LITTLE_ENDIAN  /*小头字节序*/
                          58:        u_int8_t        carp_type:4,  /*该成员只在函数carp_send_ad(定时发送CARP广告)中进行了
                              填充CARP_ADVERTISEMENT常量.其他就没用过了,实际上该成员是留着以后可以进行其他的扩充.*/
                          59:                        carp_version:4;/*版本号,即CARP_VERSION常量在发送时被填充,在接收时
                              函数carp_input_c(即接收包处理函数)会进行判断.如果对方主机发送的CARP包的版本号不为CARP_VERSION
                              时会丢弃该包.我们看看原形:if (ch->carp_version != CARP_VERSION).在本机发送CARP时,该成员也必须
                              填充该值*/
                          60: #endif
                          61: #if BYTE_ORDER == BIG_ENDIAN
                          62:        u_int8_t        carp_version:4,
                          63:                        carp_type:4;
                          64: #endif
                          65:        u_int8_t        carp_vhid;        /* 虚拟主机的ID*/
                          66:        u_int8_t        carp_advskew;        /* CARP广告的毫秒级值,当主,僚两机在抢占模式时,根据
                             该值和下面的advbase秒级的值大小来确定哪台机器为主,哪台为僚机. */
                          67:        u_int8_t        carp_authlen;   /* counter+md的32位块长度,实际上从上图可以看出为7.
                             不直接填充7的原因大概是为了以后的扩充,在程序中除了发送时填充为7外,接收时根本就不判断.目前有点多余*/
                          68:        u_int8_t        carp_pad1;        /* 保留*/
                          69:        u_int8_t        carp_advbase;        /* 发出CARP广告的秒级间隔时间,和上面的advskew合用 */
                          70:        u_int16_t        carp_cksum;      /*校验和*/
                          71:        u_int32_t        carp_counter[2]; /*实际上是一个64位长的计数器.有点类似IP的序号*/
1.8          |mcbride     72:        unsigned char        carp_md[20];        /* 用SHA1进行HASH过的MAC地址 */
1.4           avsm        73: } __packed;  /*说明是紧凑型*/
1.1           mcbride     74: 
                          75: #define        CARP_DFLTTL                255 /*发送CARP时的IP包的TTL的值,懂IP协议的人都知道
                              TTL值经过路由器时会被减1,在CARP接收程序中会判断该IP包的TTL值是否小于255,如果小于的话,说明该负载CARP
                              的IP包来自于外网,是个可疑的包,因为CARP本身是基于内网的.*/
                          76: 
                          77: /* carp_version */
                          78: #define        CARP_VERSION                2  /*CARP的版本*/
                          79: 
                          80: /* carp_type */
                          81: #define        CARP_ADVERTISEMENT        0x01 /*CARP包的类型,注意目前只有该一种类型,我们可以对其
                              进行扩展.如在CARP防火墙冗余系统中加入加密后的过滤规则的传送,好处是在两台有CARP冗余系统的OPENBSD中随便
                              哪台上设置后,在一台出现故障后,另一台能自动获取最新设置的过滤规则.*/
                          82: 
                          83: #define        CARP_KEY_LEN                20        /* 用于本地的某一CARP虚拟设备的MAC地址
                              HASH后产生的值的长度.在carp_softc的成员表示为sc_key[CARP_KEY_LEN],该值的填充一般是在CARP设备初始化
                              时对MAC地址HASH后填充到这个20字节长的区域中.*/
                          84: 
                          85: /* carp_advbase */
                          86: #define        CARP_DFLTINTV                1
                          87: 
                          88: /*
                          89:  * 统计数据.
                          90:  */
                          91: struct carpstats {
1.6           mcbride     92:        u_int64_t        carps_ipackets;                /* IPV4版本的进入的包数 */
                          93:        u_int64_t        carps_ipackets6;        /* IPV6版本的进入的包数 */
                          94:        u_int64_t        carps_badif;                /* 如果接收包的接口无CARP,则该成员加1 */
                          95:        u_int64_t        carps_badttl;                /* TTL不是CARP_DFLTTL,目前只是为255 */
                          96:        u_int64_t        carps_hdrops;                /* 如果mbuf的长度小于IP头+CARP结构长 */
                          97:        u_int64_t        carps_badsum;                /* 校验和错 */
                          98:        u_int64_t        carps_badver;                /* 错误的版本号 */
                          99:        u_int64_t        carps_badlen;                /* 得到的IP+CARP>分组的长度错误 */
                         100:        u_int64_t        carps_badauth;                /* 错误的HASH内容长度值,应该为7 */
                         101:        u_int64_t        carps_badvhid;                /* 错误的虚拟主机ID */
                         102:        u_int64_t        carps_badaddrs;                /* 居然没使用过 */
                         103: 
                         104:        u_int64_t        carps_opackets;                /* IPV4版本的发出的包数 */
                         105:        u_int64_t        carps_opackets6;        /* IPV6版本的发出的包数 */
                         106:        u_int64_t        carps_onomem;                /* 在发送CARP包时申请内存失败 */
                         107:        u_int64_t        carps_ostates;                /* 也居然没用过 */
1.1           mcbride    108: 
1.6           mcbride    109:        u_int64_t        carps_preempt;                /* 也没用过 */
1.1           mcbride    110: };
                         111: 
                         112: /*
                         113:  * 用于SIOCSVH SIOCGVH,即设置(获取)机器的carp设备的ID和广告间隔值等参数
                         114:  */
                         115: struct carpreq { /*该结构被用来放置从用户区传来的参数*/
                              /*在语句copyin(ifr->ifr_data, &carpr, sizeof carpr)中即是从用户区拷贝数据到核心区的该结构中(carpr就是*/
                              /*carpreq结构的一个实例),在随后的程序中就要使用其各个成员来设置carp设备的carp_softc属性*/
                         116:        int                carpr_state;/*该值是用户将要设置(在进行SIOCSVH时)时欲改变成哪种状态*/
                              /*如果本机carp设备已经启用了并且要更改的状态和现有的状态一样,则不会改变什么.*/
                         117: #define        CARP_STATES        "INIT", "BACKUP", "MASTER"
                         118: #define        CARP_MAXSTATE        2
                         119:        int                carpr_vhid;
                         120:        int                carpr_advskew;
                         121:        int                carpr_advbase;
                         122:        unsigned char        carpr_key[CARP_KEY_LEN];
                         123: };
                         124: #define        SIOCSVH        _IOWR('i', 245, struct ifreq)
                         125: #define        SIOCGVH        _IOWR('i', 246, struct ifreq)
                         126: 
                         127: /*
                         128:  * Names for CARP sysctl objects
                         129:  */
                         130: #define        CARPCTL_ALLOW                1        /* 在carp_input函数中,判断CARP接口是否允许接收CARP包 */
                         131: #define        CARPCTL_PREEMPT                2        /* 抢占模式 */
                         132: #define        CARPCTL_LOG                3        /* 记录错误的包 */
                         133: #define        CARPCTL_ARPBALANCE        4        /* 均衡ARP回应 */
                         134: #define        CARPCTL_MAXID                5
                         135: 
                         136: #define        CARPCTL_NAMES { 
                         137:        { 0, 0 }, 
                         138:        { "allow", CTLTYPE_INT }, 
                         139:        { "preempt", CTLTYPE_INT }, 
                         140:        { "log", CTLTYPE_INT }, 
                         141:        { "arpbalance", CTLTYPE_INT }, 
                         142: }
                         143: 
                         144: #ifdef _KERNEL   /*说明下面的函数在核心中使用,属于全局函数*/
                         145: void            carp_ifdetach (struct ifnet *); /*卸载carp接口*/
                         146: void            carp_input (struct mbuf *, ...);/*由以太网通用例程ether_input函数调用*/
1.5           mcbride    147: void            carp_carpdev_state(void *);/*新加的函数,还没看过,估计是查看CARP虚拟设备状态,统计等*/
1.2           mcbride    148: int             carp6_input (struct mbuf **, int *, int);/*IPV6用*/
1.1           mcbride    149: int             carp_output (struct ifnet *, struct mbuf *, struct sockaddr *,
                         150:                     struct rtentry *);
                         151: int             carp_iamatch (void *, struct in_ifaddr *, struct in_addr *,
                         152:                     u_int8_t **);
1.2           mcbride    153: struct ifaddr  *carp_iamatch6(void *, struct in6_addr *);
                         154: void           *carp_macmatch6(void *, struct mbuf *, struct in6_addr *);
1.1           mcbride    155: struct ifnet        *carp_forus (void *, void *);
                         156: int             carp_sysctl (int *, u_int,  void *, size_t *, void *, size_t);
                         157: #endif
阅读(2704) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~