Zebra资源

http://blog.sina.com.cn/s/blog_87bbe37e01015nlf.html

http://blog.sina.com.cn/s/blog_87bbe37e01015nly.html

 

 

zebra分析之一

(2013-01-13 10:02:13)
标签:

杂谈

分类: Zebra/Zebos

此文并不针对zebra的应用,甚至不是一个架构的分析,只是对于Zebra的一点儿思考。
Zebra 设计得是如此简洁明快。每一种数据结构均对应于一定的应用,它们之间以一种松耦合的方式共存,而多种数据结构组成的功能模块几乎完美的结合在一起,完成了非常复杂的功能。它的设计思想就在于对C语言面向对象式的应用。
虽然很多程序均借鉴面向对象设计方式,但是Zebra的代码风格是易读的,非常易于理解和学习,与此同时,Zebra使用了丰富的数据结构,比如链表、向量、表和队列等,它的松耦合方式使得每一数据结构封装的功能模块很容易被精简剥离出来,以备我们特殊的应用。这就是我写下Think Of ZEBRA非常重要的原因!
1.ZEBRA中的thread

提起thread就会让人想起线程,Linux中的线程被称为pthread,这里的thread 不是pthread,因为它只是对线程的应用层模拟。ZEBRA借助自己的thread结构,将所有的事件(比如文件描述的读写事件,定时事件等)和对应的处理函数封装起来,并取名为struct thread。然后这些threads又被装入不同的“线程“链表挂载到名为thread_master的结构中,这样所有的操作只需要面向 thead_master。
  1. /*Thread itself. */
  2. struct thread
  3. {
  4. unsigned char type; /*thread type */
  5. struct thread *next;/*next pointer of the thread */
  6. struct thread *prev;/*previous pointer of the thread */
  7. struct thread_master *master;/*pointer to the struct thread_master.*/
  8. int (*func)(struct thread *);/* eventfunction */
  9. void *arg;/*event argument */
  10. union {
  11. int val;/*second argument of the event.*/
  12. int fd;/*file descriptor in caseof read/write.*/
  13. struct timeval sands; /*rest of time sands value.*/
  14. } u;
  15. RUSAGE_T ru; /*Indepth usage info. */
  16. };

  17. /* Linked list of thread.*/
  18. struct thread_list
  19. {
  20. struct thread *head;
  21. struct thread *tail;
  22. int count;
  23. };

  24. /* Master of the theads.*/
  25. struct thread_master
  26. {
  27. struct thread_list read;
  28. struct thread_list write;
  29. struct thread_list timer;
  30. struct thread_list event;
  31. struct thread_list ready;
  32. struct thread_list unuse;
  33. fd_set readfd;
  34. fd_set writefd;
  35. fd_set exceptfd;
  36. unsigned long alloc;
  37. };

thread_master线程管理者维护了6个“线程“队列:read、write、timer、event、ready和unuse。read 队列对应于描述符的读事件,write队列对应于描述符的写事件,timer通常为定时事件,event为自定义事件,这些事件需要我们自己在适合的时候触发,并且这类事件不需要对描述符操作,也不需要延时。ready队列通常只是在内部使用,比如read,write或event队列中因事件触发,就会把该”线程”移入ready队列进行统一处理。unuse是在一个”线程”执行完毕后被移入此队列,并且在需要创建一个新的”线程”时,将从该队列中取壳资源,这样就避免了再次申请内存。只有再取不到的情况下才进行新”线程”的内存申请。

1.2 线程管理者中的"线程"链表函数


struct thread_list是一个双向链表,对应的操作有:
//添加thread到指定的链表中的尾部
static void thread_list_add (struct thread_list *list, struct thread *thread);
//添加thread到指定的链表中指定的point前部,它在需要对链表进行排序的时候很有用
static void thread_list_add_before (struct thread_list *list,
struct thread *point,
struct thread *thread);
//在指定的链表中删除制定的thread
static struct thread *thread_list_delete (struct thread_list *list, struct thread *thread);
//释放指定的链表list中所有的thread, m 中的alloc减去释放的"线程"个数
static void thread_list_free (struct thread_master *m, struct thread_list *list);
//移除list中的第一个thread 并返回
static struct thread *thread_trim_head (struct thread_list *list);

1.3 thread中的read队列


考虑这样的应用:创建一个socket,并且需要listen在该socket上,然后读取信息,那么使用read队列是不二选择。下面是一个例子,这个例子将对标准输入文件描述符进行处理:

  1. static int do_accept (struct thread *thread)
  2. {
  3. char buf[1024] = "";
  4. int len = 0;

  5. len = read(THREAD_FD(thread),buf, 1024);
  6. printf("len:%d, %s",len, buf);
  7. return 0;
  8. }

  9. int main()
  10. {
  11. struct thread thread;

  12. //创建线程管理者
  13. struct thread_master *master= thread_master_create();

  14. //创建读线程,读线程处理的描述符是标准输入0,处理函数为do_accept
  15. thread_add_read(master,do_accept, NULL,fileno(stdin));

  16. //打印当前线程管理者中的所有线程
  17. thread_master_debug(master);

  18. // thread_fetch select所有的描述符,一旦侦听的描述符需要处理就将对应的”线程”的地址通过thread返回
  19. while(thread_fetch(master,&thread))
  20. {
  21. //执行处理函数
  22. thread_call(&thread);
  23. thread_master_debug(master);

  24. //这里为什么需要再次添加呢?
  25. thread_add_read(master,do_accept, NULL,fileno(stdin));
  26. thread_master_debug(master);
  27. }

  28. return 0;
  29. }

编译执行,得到如下的结果:
// 这里readlist链表中加入了一个"线程",其他链表为空
-----------
readlist : count [1] head [0x93241d8] tail [0x93241d8]
writelist : count [0] head [(nil)] tail [(nil)]
timerlist : count [0] head [(nil)] tail [(nil)]
eventlist : count [0] head [(nil)] tail [(nil)]
unuselist : count [0] head [(nil)] tail [(nil)]
total alloc: [1]
-----------
// 输入hello,回车
Hello

// thread_call调用do_accept进行了操作
len:6, hello

// 发现“线程“被移入了unuselist
-----------
readlist : count [0] head [(nil)] tail [(nil)]
writelist : count [0] head [(nil)] tail [(nil)]
timerlist : count [0] head [(nil)] tail [(nil)]
eventlist : count [0] head [(nil)] tail [(nil)]
unuselist : count [1] head [0x93241d8] tail [0x93241d8]
total alloc: [1]
-----------

//再次调用thread_add_read发现unuselist被清空,并且”线程“再次加入readlist
-----------
readlist : count [1] head [0x93241d8] tail [0x93241d8]
writelist : count [0] head [(nil)] tail [(nil)]
timerlist : count [0] head [(nil)] tail [(nil)]
eventlist : count [0] head [(nil)] tail [(nil)]
unuselist : count [0] head [(nil)] tail [(nil)]
total alloc: [1]
-----------

1.4 thread_fetch 和thread_process_fd


顾名思义,thread_fetch是用来获取需要执行的线程的,它是整个程序的核心。这里需要对它进行重点的分析。

  1. struct thread *thread_fetch(struct thread_master*m, struct thread*fetch)
  2. {
  3. int num;
  4. int ready;
  5. struct thread *thread;
  6. fd_set readfd;
  7. fd_set writefd;
  8. fd_set exceptfd;
  9. struct timeval timer_now;
  10. struct timeval timer_val;
  11. struct timeval *timer_wait;
  12. struct timeval timer_nowait;

  13. timer_nowait.tv_sec =0;
  14. timer_nowait.tv_usec =0;

  15. while(1)
  16. {
  17. /*最先处理event队列 */
  18. if((thread= thread_trim_head(&m->event))!= NULL)
  19. return thread_run(m,thread, fetch);

  20. /*接着处理timer队列 */
  21. gettimeofday(&timer_now,NULL);
  22. for(thread= m->timer.head;thread; thread =thread->next)
  23. {
  24. /*所有到时间的线程均将被处理 */
  25. if(timeval_cmp(timer_now,thread->u.sands)>= 0)
  26.   {
  27.   thread_list_delete(&m->timer,thread);
  28.   return thread_run(m,thread, fetch);
  29. }
  30. }

  31. /*处理ready中的线程 */
  32. if((thread= thread_trim_head (&m->ready))!= NULL)
  33. return thread_run(m,thread, fetch);

  34. /*Structure copy. */
  35. readfd = m->readfd;
  36. writefd = m->writefd;
  37. exceptfd = m->exceptfd;

  38. /*Calculate select wait timer.*/
  39. timer_wait = thread_timer_wait(m,&timer_val);

  40. /*对所有描述符进行listen */
  41. num = select(FD_SETSIZE,&readfd, &writefd, &exceptfd, timer_wait);
  42. xprintf("select num:%d\n",num);
  43. if(num== 0)
  44. continue;

  45. if(num<</span> 0)
  46. {
  47.   if(errno== EINTR)
  48.   continue;
  49.   return NULL;
  50. }

  51. /*处理read中线程 */
  52. ready = thread_process_fd(m,&m->read,&readfd, &m->readfd);

  53. /*处理 write中线程 */
  54. ready = thread_process_fd(m,&m->write,&writefd, &m->writefd);

  55. if((thread= thread_trim_head(&m->ready))!= NULL)
  56. return thread_run(m,thread, fetch);
  57. }
  58. }

显然,Zebra中的thread机制并没有真正的优先级,而只是在处理的时候有些处理一些队列。他们的次序是:event、timer、 ready、 read和write。后面代码分析会得出read和write并没有明显的先后,因为它们最终都将被移入ready然后再被依次执行。而select同时收到多个描述符事件的概率是很低的。

thread_process_fd对于read和write线程来说是另一个关键的函数。

  1. Int thread_process_fd(struct thread_master *m,struct thread_list *list,
  2. fd_set *fdset,fd_set *mfdset)
  3. {
  4. struct thread *thread;
  5. struct thread *next;
  6. int ready =0;
  7. for (thread= list->head;thread; thread =next)
  8. {
  9. next =thread->next;
  10. if (FD_ISSET(THREAD_FD (thread),fdset))
  11. {
  12. assert (FD_ISSET (THREAD_FD(thread),mfdset));
  13. FD_CLR(THREAD_FD (thread),mfdset);
  14. // 将侦听到的描述符对应的线程移到ready链表中
  15. thread_list_delete (list,thread);
  16. thread_list_add (&m->ready,thread);
  17. thread->type= THREAD_READY;
  18. ready++;
  19. }
  20. }
  21. return ready;
  22. }

Thread_process_fd 将侦听到的描述符对应的线程移到ready链表中,并且进行文件描述的清除操作,文件描述符的添加在thread_add_read和thread_add_write中进行。

1.5 thread中的其他链表

write链表的操作类似于read链表,而event链表是直接操作的。timer链表只是添加对时间的比对操作。
在加入对应的链表时,使用不同的添加函数。
struct thread *
thread_add_read (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd);
struct thread *thread_add_write (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd);
struct thread *thread_add_event (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd);
struct thread *thread_add_timer (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd);

1.6 thread 机制中的其他函数

//执行thread
void thread_call (struct thread *thread);

//直接创建并执行,m参数可以为NULL
struct thread *thread_execute (struct thread_master *m,
int (*func)(struct thread *), void *arg, int val);

//取消一个线程,thread中的master指针不可为空
void thread_cancel (struct thread *thread);

//取消所有event链表中的参数为arg的线程
void thread_cancel_event (struct thread_master *m, void *arg);

//类似于thread_call,区别是thread_call只是执行,不将其加入unuse链表。thread_run执行后会将其加入unuse链表。
struct thread *thread_run (struct thread_master *m, struct thread *thread, struct thread *fetch);

// 释放m及其中的线程链表
void thread_master_free (struct thread_master *m);

1.7 一些时间相关的函数

static struct timeval timeval_subtract (struct timeval a, struct timeval b);

static int timeval_cmp (struct timeval a, struct timeval b);
当然也提供了简单的DEBUG函数thread_master_debug。

2.对ZEBRA中thread的应用

对thread的应用的探讨是最重要的,也是最根本的。ZEBRA的thread机制,模拟了线程,便于平台间的移植,使流水线式的程序编码模块化,结构化。


线程列表间的组合很容易实现状态机的功能。可以自定义应用层通信协议。比如我们定义一个sysstat的远程监控协议。 Client请求Server,请求Code 可以为SYS_MEM,SYS_RUNTIME,SYS_LOG等信息获取动作,也可以是SYS_REBOOT,SYS_SETTIME等动作请求, Server回应这个SYS_MEM等的结果。通常这很简单,但是如果我们需要添加一些步骤,比如用户验证过程呢?


  1. Request Auth
  2. Client-------------------------------->Server
  3.   Response PWD?
  4. Client<</span>--------------------------------Server
  5.   Provide PWD
  6. Client-------------------------------->Server
  7. Auth Result
  8. Client<</span>--------------------------------Server
  9.   SYS_LOG
  10. Client-------------------------------->Server
  11.   SYS_LOG_INFO
  12. Client<</span>--------------------------------Server

再考虑三次认证错误触发黑名单事件!状态机就是在处理完上一事件后,添加不同的事件线程。


3.对ZEBRA的思考

Zebra由Kunihiro Ishiguro开发于15年前,Kunihiro Ishiguro离开了Zebra,而后它的名字被改成了quagga,以至于在因特网上输入Zebra后,你得到只有斑马的注释。Zebra提供了一整套基于TCP/IP网络的路由协议的支持,如RIPv1,RIPv2的,RIPng,OSPFv2,OSPFv3,BGP等,然而它的亮点并不在于此,而在于它对程序架构的组织,你可以容易的剥离它,使他成为专用的cli程序,也已可以轻易的提取其中的一类数据结构,也可以借用他的thread机制实现复杂的状态机。

编码的价值往往不在于写了多少,而在于对他们的组织!好的组织体现美好的架构、设计的艺术,可以给人启迪,并在此基础上激发出更多的灵感。如果一个初学者想学习程序设计的架构,无疑选择Zebra是一个明智的选择,你不仅可以学到各种数据结构,基于C的面向对象设计,还有CLI,以及各种网络路由协议,最重要是的Zebra条理清晰,代码紧凑,至少不会让你焦头烂额

 

zebra分析之二-zebra thread

(2013-01-13 10:20:19)
标签:

杂谈

分类: Zebra/Zebos

1 zebra线程机制概述

zebra这个软件包整体结构大致可分为两大块:协议模块和守护进程模块。协议模块实现各协议的功能,各协议以子模块的形式加载到zebra中;守护进程模块的功能主要是管理各协议的信令传输、表项操作、系统操作调用等事务,为各协议提供底层信息以及相关的硬件处理等功能支持。Zebra与各协议的交互采用的是C-S模式,在每个协议子模块中均有一个Client端与守护进程模块中的Server端交互,它们所使用的socketzebra内部使用的socket,不与外部交互。

zebra中的线程是分队列调度的,每个队列以一个链表的方式实现。线程队列可以分成五个队列:eventtimerreadyreadwrite。队列的优先级由高到低排列。但是,readwrite队列并不参与到优先级的排列中,实际操作时,如果readwrite队列中的线程就绪,就加入ready队列中,等待调度。调度时,首先进行event队列中线程的调度,其次是timerready

实际上,zebra中的线程是“假线程”,它并没有实现线程中的优先级抢占问题。在zebra的线程管理中,有个虚拟的时间轴,必须等前一时间的线程处理完,才看下一时间的线程是否触发。由于电脑处理速度较快且处理每个线程的时间间隔较小,所以处理其多线程来可以打到类似“并行处理”的效果。

zebra源码中有关线程管理的各个函数放置于zebra-0.95a\lib文件夹的thread.hthread.c两个文件中。

2线程管理源码分析

2.1重要结构体介绍

2.1.1thread

这是线程队列中每一个单个线程的代码描述,线程队列被描述成双向链表的形式,thread结构体是双向链表的元素。共有六种线程:readwritetimereventreadyunused。因此,线程队列也有六种。

struct thread

{

unsigned char type;

struct thread *next;

struct thread *prev;

struct thread_master*master;

int (*func) (struct thread *);

void *arg;

union {

int val;

int fd;

struct timeval sands;

} u;

RUSAGE_T ru;

};

2.1.2thread_list

一个thread_list结构体描述一个thread双向链表,也即一个进程队列。

struct thread_list

{

struct thread *head;

struct thread *tail;

int count;

};

2.1.3thread_master

总的线程管理结构体,里面存有六种线程队列,三种文件描述符以及占用空间信息。

struct thread_master

{

//六种线程队列

struct thread_list read;

struct thread_list write;

struct thread_list timer;

struct thread_list event;

struct thread_list ready;

struct thread_list unuse;

//三种文件描述符

fd_set readfd;

fd_set writefd;

fd_set exceptfd;

//thread_master所占空间大小

unsigned long alloc;

};

Zebra分析之三-详解Thread机制

(2013-01-13 10:16:17)
标签:

杂谈

分类: Zebra/Zebos

1.thread的四种创建方法

一个新的thread可以通过如下三种方式被创建,主要是看你需要创建的thread的类型:

1, thread_add_read:添加一个thread到read queue,该thread负责通过socket接受和读取从client端来的数据。

2, thread_add_write:添加一个thread到write queue,该thread负责通过socket向client端填充和写数据。

3, thread_add_timer function calls:添加一个thread到timer queue,该thread负责定时一个event,例如update和redistribute一个route table.

4, thread_add_event:添加一个event thread到event queue。

上面这三个函数的处理过程都差不多:

1, 创建thread。首先在unuse queue查找,如果有unuse thread,就使用它,否则重新分配空间。

2, 根据参数,对thread进行赋值。

3, 将该thread加入到相应的queue中。

2. thread的调用

1,bgp daemon不断地从event queue中取出thread并且执行它。一旦该thread被执行了,将该thread的type设置为unuse。并且将该thread添加到unuse queue中。

2,如果event queue为空时,bgp daemon 通过select函数监控读、写、异常三个描述符集。一旦有某个描述符准备就绪,则将该描述符所对应的thread加入ready queue.

而对于timer queue中的thread,只有当select函数超时后才会进入ready queue.

3.zebrad端的thread

zebrad启动后会,在read queue中会出现两个thread,一个是等待来自local client端bgpd的连接,另一个是等待来自vty client端的连接。

第1个thread

zebra_init ( )-> zebra_serv_un ( )中创建一个thread,加入read queue。该thread的处理函数为zebra_accept,监听内部client的socket。

zebra_client_create (client_sock);创建一个新的zebra client

zebra_event (ZEBRA_SERV, accept_sock, NULL);继续监听server socket

puts("<-zebra_accept");

return 0;

}

vty_accept,加入read queue。作为vty server监听internet的vty socket.。

vty_create(vty_sock, &su);

根据vty_sock和ip地址信息su,创建一个新的vty。

puts("<-vty_accept");

return 0;

}

vty_flush

vty_event (VTY_READ, vty_sock, vty);

根据new client vty的vty_sock创建新的VTY_READ thread,加入read queue,该thread的处理函数为vty_read

return vty;

}

sockunion_bind(accept_sock, &su, port, NULL);

将accept_socket文件描述符与一个特定的逻辑网络连系起来。服务器端使用su->sin.sin_addr.s_addr = htonl (INADDR_ANY);表示接受任何一个主机网络接口上的连接请求。

if (ret < 0)

{

close (accept_sock);

return;

}

ret = listen (accept_sock, 3);

将accept_sock套接口设置成被动监听状态,用于接受连接,只能在服务器端使用。

if (ret < 0)

{

zlog (NULL, LOG_WARNING, "can't listen socket");

close (accept_sock);

return;

}

vty_event(VTY_SERV, accept_sock, NULL);

}

sockunion_bind操作

int sockunion_bind (int sock, union sockunion *su, unsigned short port,

union sockunion *su_addr)

{

int size = 0;

int ret;

if (su->sa.sa_family == AF_INET)

{

size = sizeof (struct sockaddr_in);

su->sin.sin_port = htons (port);

#ifdef HAVE_SIN_LEN

su->sin.sin_len = size;

#endif

if (su_addr == NULL)

su->sin.sin_addr.s_addr = htonl (INADDR_ANY);

服务器一般将sin_addr.s_addr 字段设置为INADDR_ANY表示套接字应接收任何一个主机网络接口上的连接请求。

客户端将sin_addr.s_addr字段设置为服务器主机的IP地址。

}

#ifdef HAVE_IPV6

else if (su->sa.sa_family == AF_INET6)

{

size = sizeof (struct sockaddr_in6);

su->sin6.sin6_port = htons (port);

#ifdef SIN6_LEN

su->sin6.sin6_len = size;

#endif

if (su_addr == NULL)

{

#if defined(LINUX_IPV6) || defined(NRL)

  bzero (&su->sin6.sin6_addr, sizeof (struct in6_addr));

#else

  su->sin6.sin6_addr = in6addr_any;

#endif

}

}

#endif 

ret = bind (sock, (struct sockaddr *)su, size);

if (ret < 0)

zlog (NULL, LOG_WARNING, "can't bind socket : %s", strerror (errno));

return ret;

}

vty_event操作

static void vty_event (enum event event, int sock, struct vty *vty)

{

struct thread *vty_serv_thread;

switch (event)

{

case VTY_SERV:

vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);

vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);

break;

case VTY_READ:

vty->t_read = thread_add_read (master, vty_read, vty, sock);

if (vty->v_timeout)

{

  if (vty->t_timeout)

    thread_cancel (vty->t_timeout);

  vty->t_timeout =

  thread_add_timer (master, vty_timeout, vty, vty->v_timeout);

}

break;

case VTY_WRITE:

if (! vty->t_write)

vty->t_write = thread_add_write (master, vty_flush, vty, sock);

break;

case VTY_TIMEOUT_RESET:

if (vty->t_timeout)

{

  thread_cancel (vty->t_timeout);

  vty->t_timeout = NULL;

}

if (vty->v_timeout)

{

  vty->t_timeout =

  thread_add_timer (master, vty_timeout, vty, vty->v_timeout);

}

break;

}

}

第2 个thread

bgp_serv_sock( )-> bgp_serv_sock_family( )中创建一个thread,不是通过event的方式添加的。该thread的处理函数为bgp_accept。作为bgp_server,接受来自 internet上的bgp连接。

bgp_serv_sock_family操作

port = 179 family = AF_INET

void bgp_serv_sock_family (unsigned short port, int family)

{

int ret;

int bgp_sock;

union sockunion su;

bzero (&su, sizeof (union sockunion));

su.sa.sa_family = family;

bgp_sock = sockunion_stream_socket (&su); 产生一个BGP socket

sockopt_reuseaddr (bgp_sock);

sockopt_reuseport (bgp_sock);

ret = sockunion_bind (bgp_sock, &su, port, NULL);

ret = listen (bgp_sock, 3);

if (ret < 0)

{

zlog (NULL, LOG_INFO, "Can't listen bgp server socket : %s",

  strerror (errno));

return;

}

thread_add_read (master, bgp_accept, NULL, bgp_sock); 添加一个thread 到readlist中。

}

VTY server和BGP server 在使用accept操作的方法如下:

他们均是通过创建一个THREAD_READ 类型的thread,加到Master的readlist的队列后面,thread 的处理函数会执行accept操作。

VTY accept:

vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);

BGP server accept:

thread_add_read (master, bgp_accept, NULL, bgp_sock);

bgpd和zebrad间通信

bgp和zebra是通过zebra message进行通信,格式如下:

报文头3字节(前两字节length,后1字节为command type)

报文体长度不定。

#define ZEBRA_INTERFACE_ADD 1

#define ZEBRA_INTERFACE_DELETE 2

#define ZEBRA_INTERFACE_ADDRESS_ADD3

#define ZEBRA_INTERFACE_ADDRESS_DELETE4

#define ZEBRA_INTERFACE_UP 5

#define ZEBRA_INTERFACE_DOWN 6

#define ZEBRA_IPV4_ROUTE_ADD 7

#define ZEBRA_IPV4_ROUTE_DELETE 8

#define ZEBRA_IPV6_ROUTE_ADD 9

#define ZEBRA_IPV6_ROUTE_DELETE 10

#define ZEBRA_REDISTRIBUTE_ADD 11

#define ZEBRA_REDISTRIBUTE_DELETE 12

#define ZEBRA_REDISTRIBUTE_DEFAULT_ADD13

#define ZEBRA_REDISTRIBUTE_DEFAULT_DELETE 14

#define ZEBRA_IPV4_NEXTHOP_LOOKUP 15

#define ZEBRA_IPV6_NEXTHOP_LOOKUP 16

bgpd和zebrad之间的api接口

bgp端接受到message后,会执行相应的bgp action:

bgp action func  message type

int (*interface_add) (int, struct zclient *, zebra_size_t);ZEBRA_INTERFACE_ADD

int (*interface_delete) (int, struct zclient *, zebra_size_t); ZEBRA_INTERFACE_DELETE

int (*interface_up) (int, struct zclient *, zebra_size_t);   ZEBRA_INTERFACE_UP

int (*interface_down) (int, struct zclient *, zebra_size_t);ZEBRA_INTERFACE_DOWN

int (*interface_address_add) (int, struct zclient *, zebra_size_t); ZEBRA_INTERFACE_ADDRESS_ADD

int (*interface_address_delete) (int, struct zclient *, zebra_size_t);ZEBRA_INTERFACE_ADDRESS_DELETE

int (*ipv4_route_add) (int, struct zclient *, zebra_size_t); ZEBRA_IPV4_ROUTE_ADD

int (*ipv4_route_delete) (int, struct zclient *, zebra_size_t); ZEBRA_IPV4_ROUTE_DELETE

int (*ipv6_route_add) (int, struct zclient *, zebra_size_t); ZEBRA_IPV6_ROUTE_ADD

int (*ipv6_route_delete) (int, struct zclient *, zebra_size_t); ZEBRA_IPV6_ROUTE_DELETE

zebra 端接受到message后,会执行相应的zebra action:

zebra action func   message type

void zread_interface_add (struct zserv *client, u_short length)   ZEBRA_INTERFACE_ADD

void zread_interface_delete (struct zserv *client, u_short length) ZEBRA_INTERFACE_DELETE

void zread_ipv4_add (struct zserv *client, u_short length) ZEBRA_IPV4_ROUTE_ADD

void zread_ipv4_delete (struct zserv *client, u_short length) ZEBRA_IPV4_ROUTE_DELETE

void zread_ipv6_add (struct zserv *client, u_short length) ZEBRA_IPV6_ROUTE_ADD

void zread_ipv6_delete (struct zserv *client, u_short length) ZEBRA_IPV6_ROUTE_DELETE

void zebra_redistribute_add (int command, struct zserv *client, int length) ZEBRA_REDISTRIBUTE_ADD

void zebra_redistribute_delete (int command, struct zserv *client, int length) ZEBRA_REDISTRIBUTE_DELETE

voidzebra_redistribute_default_add (int command,

struct zserv *client, int length) ZEBRA_REDISTRIBUTE_DEFAULT_ADD

void zebra_redistribute_default_delete (int command,

struct zserv *client, int length) ZEBRA_REDISTRIBUTE_DEFAULT_DELETE

void zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) ZEBRA_IPV4_NEXTHOP_LOOKUP

void zread_ipv6_nexthop_lookup (struct zserv *client, u_short length) ZEBRA_IPV6_NEXTHOP_LOOKUP

bgp action:将local_client_socket中数据,写入bgp数据库。

zebra action:将zebra数据库中的信息写入local_server_subsocket,让local client端进行读取。

bgp peer间通信

bgp_accept操作

int bgp_accept (struct thread *thread)

{

int bgp_sock;

int accept_sock;

union sockunion su;

struct peer *peer;

struct peer *peer1;

char buf[SU_ADDRSTRLEN];

accept_sock = THREAD_FD (thread);

printf("->bgp_accept [%d]\n",accept_sock);

thread_add_read (master, bgp_accept, NULL, accept_sock);

bgp_sock = sockunion_accept (accept_sock, &su);

if (bgp_sock < 0)

{

zlog_err ("[Error] BGP socket accept failed (%s)", strerror (errno));

  printf("[Error] BGP socket accept failed (%s)", strerror (errno));

  puts("<-bgp_accept 2");

return -1;

}

if (BGP_DEBUG (events, EVENTS))

zlog_info ("[Event] BGP connection from host %s", inet_sutop (&su, buf));

printf("[Event] BGP connection from host %s", inet_sutop (&su, buf));

peer1 = peer_lookup_by_su (&su);

if (! peer1 || peer1->status == Idle)

{

if (BGP_DEBUG (events, EVENTS))

{

  if (! peer1)

  zlog_info ("[Event] BGP connection IP address %s is not configured",

 inet_sutop (&su, buf));

  else

    zlog_info ("[Event] BGP connection IP address %s is Idle state",

 inet_sutop (&su, buf));

}

close (bgp_sock);

puts("<-bgp_accept 2");

return -1;

}

if (BGP_DEBUG (events, EVENTS))

zlog_info ("[Event] Make dummy peer structure until read Open packet");

printf("[Event] Make dummy peer structure until read Open packet\n");

{

char buf[SU_ADDRSTRLEN + 1];

peer = peer_create_accept ();

SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER);

peer->su = su;

peer->fd = bgp_sock;

peer->status = Active;

sockunion2str (&su, buf, SU_ADDRSTRLEN);

peer->host = strdup (buf);

}

BGP_EVENT_ADD (peer, TCP_connection_open); 创建一个event thread执行bgp_event函数

puts("<-bgp_accept 0");

return 0;

}

bgp_event操作

int bgp_event (struct thread *thread)

{

int ret;

int event;

int next;

struct peer *peer;

peer = THREAD_ARG (thread); // get peer

event = THREAD_VAL (thread); // get FSM eventeg.TCP_connection_open

puts("->bgp_event");

next = FSM [peer->status -1][event - 1].next_state;next为下一个状态

if (BGP_DEBUG (fsm, FSM))

plog_info (peer->log, "%s [FSM] %s (%s->%s)", peer->host,

  bgp_event_str[event],

  LOOKUP (bgp_status_msg, peer->status),

  LOOKUP (bgp_status_msg, next));

printf("%s [FSM] %s (%s->%s)", peer->host,

  bgp_event_str[event],

  LOOKUP (bgp_status_msg, peer->status),

  LOOKUP (bgp_status_msg, next));

ret = (*(FSM [peer->status - 1][event - 1].func))(peer); 执行本状态处理函数

if (ret < 0)

  {

  puts("<-bgp_event 1");

return ret;

  }

if (next != peer->status) 判断状态是否需要转变

fsm_change_status (peer, next);


bgp_timer_set (peer);

puts("<-bgp_event 0");

return 0;

}

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值