zebra 的Thread機制 -- 003

原文鏈接:https://www.cnblogs.com/iplus/archive/2013/03/18/4467311.html

==========================================================

一、線程機制概述

zebra這個軟件包整體結構大致可分爲兩大塊:協議模塊和守護進程模塊。協議模塊實現各協議的功能,各協議以子模塊的形式加載到zebra中;守護進程模塊的功能主要是管理各協議的信令傳輸、表項操作、系統操作調用等事務,爲各協議提供底層信息以及相關的硬件處理等功能支持。Zebra與各協議的交互採用的是C-S模式,在每個協議子模塊中均有一個Client端與守護進程模塊中的Server端交互,它們所使用的socket爲zebra內部使用的socket,不與外部交互。

  zebra中的線程是分隊列調度的,每個隊列以一個鏈表的方式實現。線程隊列可以分成五個列:event、timer、ready、read、write。隊列的優先級由高到低排列。但是,read和write隊列並不參與到優先級的排列中,實際操作時,如果read和write隊列中的線程就緒,就加入ready隊列中,等待調度。調度時,首先進行event隊列中線程的調度,其次是timer和ready。

實際上,zebra中的線程是“假線程”,它並沒有實現線程中的優先級搶佔問題。在zebra的線程管理中,有個虛擬的時間軸,必須等前一時間的線程處理完,纔看下一時間的線程是否觸發。由於電腦處理速度較快且處理每個線程的時間間隔較小,所以處理其多線程來可以打到類似“並行處理”的效果。

zebra源碼中有關線程管理的各個函數放置於zebra-0.95a\lib文件夾的thread.h和thread.c兩個文件中。

二、線程管理源碼分析

這是線程隊列中每一個單個線程的代碼描述,線程隊列被描述成雙向鏈表的形式,thread結構體是雙向鏈表的元素。共有六種線程:read、write、timer、event、ready、unused。因此,線程隊列也有六種。

struct thread

{

unsigned char type;        /* thread類型,共有六種 */

struct thread *next;        /* 指向下一thread的指針,雙向鏈表 */

struct thread *prev;        /*指向前一thread的指針*/

struct thread_master *master;      /* 指向該thread所屬thread_master結構體的指針 */

int (*func) (struct thread *); /* event類型thread的函數指針 */

void *arg;               /* event類型thread的參數 */

union {

    int val;                /* event類型thread的第二個參數*/

    int fd;                  /* read/write類型thread相應的文件描述符 */

    struct timeval sands;   /* 該thread的剩餘時間,timeval類型,此結構體定義在time.h中,有兩個元素,秒和微秒 */

} u;

RUSAGE_T ru;                    /* 詳細用法信息,RUSAGE這個宏在該thread有用法描述時定義爲rusage類型,描述其詳細進程資源信息,沒有用法描述時定義爲timeval類型 */

};


一個thread_list結構體描述一個thread雙向鏈表,也即一個進程隊列。

struct thread_list

{

struct thread *head;/* 該線程隊列頭指針 */

struct thread *tail; /* 該線程隊列尾指針 */

int count; /* 該線程隊列元素數目 */

};


總的線程管理結構體,裏面存有六種線程隊列,三種文件描述符以及佔用空間信息。

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;

};

1.1 相關函數簡介
下面給出了zebra關於線程管理的相關函數的簡要功能介紹。

1.1.1 thread_master_create ()
爲創建一個新的thread_master結構體動態開闢一塊內存空間。

1.1.2 thread_list_add ()
在list雙向鏈表尾部插入一個新的thread。

1.1.3 thread_list_add_before ()
在函數參數point所指向的thread前面插入一個新的thread。

1.1.4 thread_list_delete ()
刪除參數中指定的thread。

1.1.5 thread_add_unuse ()
向指定thead_master中的unused鏈表尾部插入新thread。

1.1.6 thread_list_free ()
從內存中釋放掉指定thread_master中的指定thread鏈表所佔空間。

1.1.7 thread_master_free ()
徹底釋放指定thread_master所佔內存空間。

1.1.8 thread_trim_head ()
若指定thread鏈表中非空,刪除該鏈表頭指針所指thread,並將其返回,即從線程隊列中取出一個線程。

1.1.9 thread_empty ()
判斷指定thread鏈表是否爲空。

1.1.10 thread_timer_remain_second ()
得到指定線程的剩餘時間。

1.1.11 thread_get ()
若指定thread_master中的unuse鏈表非空,從該隊列中取出一個thread,根據參數初始化並返回之。否則,給該thread_master多開闢一塊空間給新的thread,根據參數初始化該thread並返回之。

1.1.12 thread_add_read ()
根據所給參數在指定thread_master中添加並初始化一個read類型的thread並返回之。

1.1.13 thread_add_write ()
根據所給參數在指定thread_master中添加並初始化一個write類型的thread並返回之。

1.1.14 thread_add_timer ()
根據所給參數在指定thread_master中添加並初始化一個timer類型的thread。若timer鏈表不要求排序,則直接返回新thread,若要求排序,則將新thread插入到隊列的相應位置後再返回之。

1.1.15 thread_add_event ()
根據所給參數在指定thread_master中添加並初始化一個event類型的thread並返回之。

1.1.16 thread_cancel ()
刪除指定thread,刪除後將其類型置爲THREAD_UNUSED,並將其插入到該thread_master的unuse鏈表中。

1.1.17 thread_cancel_event ()
將指定thread_master的event鏈表中與參數中arg相匹配的thread刪除。

1.1.18 thread_timer_wait ()
找出指定thread_master的timer鏈表中最小的剩餘時間並將其返回。

1.1.19 thread_run ()
將指定thread的值賦給thread類型的fetch,然後將其類型置爲THREAD_UNUSED,並將其插入unuse鏈表,返回fetch。

1.1.20 thread_process_fd ()
將指定thread鏈表中的元素取出插入到該thread_master的ready鏈表中,返回該鏈表中插入元素的個數。

1.1.21 thread_fetch ()
若指定thread_master的event隊列非空取出其頭元素並用run函數處理。取出並用run函數處理timer隊列中每一個之前創建的線程。若指定thread_master的ready隊列非空取出其頭元素並用run函數處理。拷貝該thread_master的文件描述符。將read和write鏈表插到ready鏈表中,再從ready鏈表取頭元素用run函數處理。如此無限循環下去直到所有進程都處理完。

1.1.22 thread_consumed_time ()
得到該進程所耗費的時間。

1.1.23 thread_call ()
執行該thread中的功能函數,如果該thread持續時間超過CPU規定的獨佔時間,發出警告。

1.1.24 thread_execute ()
根據參數創建一個event類型的thread並用thread_call()函數對其進行處理

 

====================================================================================

一、主要從 daemon 的 thread角度 分析備忘。

注意: 具體函數功能詳見附錄,分析要結合zebra源代碼(thread.c中)。

1、每個daemon(e.g igmp-snooping、ring)都定義一個 master 的 全局變量;

2、master 內 有六個 struct thread_list *read、write 等六個 鏈表;

3、最小的 執行單元爲 thread , 必要的時候會 掛在 對應的 thread_list 上;

4、最終 最重要的是 各個daemon的 main函數最後的 while(thread_fetch(master, &thread))  thread_call(&thrad);

5、thread_fetch 功能是查找master 結構下的 幾個 struct thread_list 鏈;

6、其中, 三個鏈是 比較 重要的,struct thread_list *event、timer、ready;

7、thread_fetch  的查找順序 也是上邊這個 順序, 所以其沒有真正的 優先級 搶斷問題(真正的線程pthread);

8、查找每個鏈的過程  就是 看看 該鏈上  有沒有 準備好的 thread;

9、有的話從 該鏈上移除, 做適當的 type 等重新賦值, 然後加入到  unuse  鏈上; 《thread_run() 的執行過程》  

      注:移到 unuse 鏈上 ,而不是 直接free掉, 是爲了以後 有新的thread要掛到  event、timer、ready等鏈上的時                  候,可以直接 來 unuse 鏈上來取, 不用重新 malloc, 節省時間。

10、然後 返回查找到的 thread,然後main中的while下  調用thread_call(&thrad)  執行 該塊 thread;

11、此外 還有 兩個重要的  明示給 用戶(程序員)的  thread_list * read、write,對應 socket 的 read、write事件;

12、當有 read、write類型的socket 要加入 到list 時, 調用 thread_add_read/write 把相應的 sfd及 func以thread的形         式加入到 對應的  read 或者 write  thread_list;

13、同時,重要的是: thread_add_read 裏 只要是把 對應的  sfd 加入到 master->readfd/writefd(fd_set 類型);

14、而後 thread_fetch裏調用 select 對 read、write、error 的fd_set進行 監聽;

15、並調用兩次thread_process_fd把  有事件的fd(FD_ISSET) 對應的 thread  從 thread_list * read、write 鏈中移除;

16、並進一步加入到ready鏈中;

17、在thread_fetch 最後, 再對ready鏈中 移除頭元素,加入unuse 鏈,返回該thread塊,並等待thread_call執行;

18、對於 thread_fetch中timer鏈的查找,遍歷timer鏈中所有thread,查找超時的 thread,返回該thread:過程如下:

19、當用 thread_add_timer 添加 timer 事件時,用參數func、time、master等, 構造一個 thread;

20、其中thread->u.sands 爲 (gettimeofday + time),  是未來的 某一個時間值,遍歷timer鏈上的thread時, 再                     gettimeofday一下, 然後與 thread->u.sands對比,確定該thread 是否  超時 可執行;

21、對於thread_fetch中 監聽 read、write事件的 select 的timeout 時間問題: 函數 thread_time_wait()

22、(NO sort)遍歷 timer鏈上 所有thread,取最小的  thread->u.sands  ,與現在的gettimeofday比較,若小於(表明最                       小的thread塊已經超時)則timeout,應該給一個最小的值,免得timer事件 誤差太大,timeout=10us;

                       若大於gettimeofday(sec>=0)(還未超時),則 timeout=該值與gettimeofday之差;再while1的時候下一個                         thread正好超時一點,不會誤差太大;

                       若 timer鏈上無thread,則select 爲 阻塞。(timeout=NULL);   (沒有事件處理阻塞,進程掛起無影響)

   

二、daemon與vtysh間的通信(依賴daemon的thread機制)

注:①其間的通信主要是 vtysh從終端獲取輸入的CLI命令,然後解析,根據DEFSH或者其他發往指定daemon;

       ②vtysh進程會和每個daemon進行connect;

       ③connect分兩種, 一種是vtysh進程main中 connect_default(rcs默認啓動的daemon);

                                       二是 vtysh_send之前 會 connect 對應的 daemon

       

下邊結合 daemon (e.g igmp-snooping) 與 vtysh 進程 通信來 舉例:

1、 igmp-snooping 的 main 中,如下圖所示吧:

2、實際上 但從 發送CLI命令字符 來看, 是daemon端 維護一個 select, vtysh 端 connect及 send;

3、可待續補充......


三、vtysh端的read、write的阻塞與  數據傳輸 不丟失保證

1、待 補充.....

四、daemon端接收其他報文的socket

1、igmp-snooping 要  申請一個 g_snoop_pkt_sock=socket(AF_INET,SOCK_PACKET,htons(0800));

2、該socket 用來 接收 igmp 報文;

3、根據網上的資料,對於socket類型的文件,不顯式用fcntl (sockfd,F_SETFL,O_NONBLOCK) 設定時,默認阻塞;

4、所以 recv(g_snoop_pkt_sock, ,)  爲 阻塞的;   但該阻塞 永遠不會發生!!!  原因如下:
5、在 igmp-snooping enable中用 thread_add_read 把 g_snoop_pkt_sockadd到read 鏈裏, 即,受select監控。

6、只有在 有 igmp 報文 到達 該g_snoop_pkt_sock  時,select檢測到,並執行thread塊,即igmp_snooping_read,

      進而纔會執行到  阻塞的   recv(g_snoop_pkt_sock, ,),但此時爲已經 有報文過來,所以該recv不會阻塞;

7、結論:對於該 接收其他AF_INET報文的 socket,在該架構下, 阻塞與否 幾乎無差異!!

8、ring 中 接收ring報文 的   幾乎 和上述相同,g_ring_sock = socket(AF_INET, SOCK_PACKET, htons(0x7010));

9、下邊的 步驟 同上述 2---7, 另, ring 的 socket 用的 read。

 

=========================================================================

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

  /* Register myself. */

  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);   /* Avoid sd leak. */

      return;

    } 

  /* Listen socket under queue 3. */

  ret = listen (accept_sock, 3);

將accept_sock套接口設置成被動監聽狀態,用於接受連接,只能在服務器端使用。

  if (ret < 0)

    {

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

      close (accept_sock);   /* Avoid sd leak. */

      return;

    } 

  /* Add vty server event. */

  vty_event(VTY_SERV, accept_sock, NULL);

sockunion_bind操作

/* Bind socket to specified address. */

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

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

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

       }

    }

#endif /* HAVE_IPV6 */ 

  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操作 

/* struct thread_master *master; */

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);

      /* Time out treatment. */

      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

/* Make bgpd's server socket. */

void bgp_serv_sock_family (unsigned short port, int family)

{

  int ret;

  int bgp_sock;

  union sockunion su;

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

  /* Specify address family. */

  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)

報文體長度不定。

 

/* Zebra message types. */

#define ZEBRA_INTERFACE_ADD                1

#define ZEBRA_INTERFACE_DELETE             2

#define ZEBRA_INTERFACE_ADDRESS_ADD        3

#define ZEBRA_INTERFACE_ADDRESS_DELETE     4

#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_ADD    13

#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操作

/* Accept bgp connection. */

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];

 

  /* Regiser accept thread. */

  accept_sock = THREAD_FD (thread);

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

  thread_add_read (master, bgp_accept, NULL, accept_sock);

 

  /* Accept client connection. */

  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));

  /* Check remote IP address */

  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;

    }

 

  /* Make dummy peer until read Open packet. */

  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;

 

    /* Make peer's address string. */

    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操作

/* Execute event process. */

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 event  eg.TCP_connection_open

  puts("->bgp_event");

  /* Logging this 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));

 

  /* Call function. */

  ret = (*(FSM [peer->status - 1][event - 1].func))(peer); 執行本狀態處理函數

 

  /* When function do not want proceed next job return -1. */

  if (ret < 0)

      {

      puts("<-bgp_event 1");

    return ret;

      }

  /* If status is changed. */

  if (next != peer->status)               判斷狀態是否需要轉變

    fsm_change_status (peer, next);

 

  /* Make sure timer is set. */

  bgp_timer_set (peer);

  puts("<-bgp_event 0");

  return 0;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章