quagga源碼分析--大內總管zebra


zebra,中文翻譯是斑馬,於是我打開了宋冬野的《斑馬,斑馬》作為BGM來完成這個篇章,嘿嘿,小資一把!

zebra姑且戲稱它是quagga項目的大內總管。

因為它負責管理其他所有協議進程的路由信息的更新與交互,並負責與內核交換信息,如下的架構:

1 +----+  +----+  +-----+  +-----+
2 |bgpd|  |ripd|  |ospfd|  |zebra|
3 +----+  +----+  +-----+  +-----+
4                             |
5 +---------------------------|--+
6 |                           v  |
7 |  UNIX Kernel  routing table  |
8 |                              |
9 +------------------------------+

 

好了,簡介完了,開始看代碼吧:

 

1、zebra作為其他協議進程的服務端:

1 /* Make zebra server socket, wiping any existing one (see bug #403). */
2 void
3 zebra_zserv_socket_init(char *path) {
4 #ifdef HAVE_TCP_ZEBRA
5     zebra_serv();
6 #else
7     zebra_serv_un(path ? path : ZEBRA_SERV_PATH);
8 #endif /* HAVE_TCP_ZEBRA */
9 }

zebra綁定了(loopback,2600)的地址和端口,並開始監聽socket,同時加入到thread事件ZEBRA_SERV當中,來接收客戶端發送過來的路由信息:

 1 accept_sock = socket(AF_INET, SOCK_STREAM, 0);
 2 
 3 addr.sin_family = AF_INET;
 4 
 5 addr.sin_port = htons(ZEBRA_PORT);
 6 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 7     addr.sin_len = sizeof(struct sockaddr_in);
 8 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 9 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
10 
11 ret  = bind(accept_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
12   
13 ret = listen(accept_sock, 1);

zebra_event(ZEBRA_SERV, accept_sock, NULL);

2、客戶端(比如isis協議):

2.1 創建客戶端,並在初始化加入到thread事件調度當中去。

1 void
2 isis_zebra_init(struct thread_master *master) {
3     zclient = zclient_new(master);
4     zclient_init(zclient, ZEBRA_ROUTE_ISIS);
5     
6     ......
7 
8     return;
9 }
 1 void
 2 zclient_init (struct zclient *zclient, int redist_default)
 3 {
 4   int i;
 5   /* Enable zebra client connection by default. */
 6   zclient->enable = 1;
 7   /* Set -1 to the default socket value. */
 8   zclient->sock = -1;
 9   .....
10   zclient_event (ZCLIENT_SCHEDULE, zclient);
11 }  
 1 static void
 2 zclient_event(enum event event, struct zclient *zclient) {
 3     switch (event) {
 4     case ZCLIENT_SCHEDULE:
 5         if (!zclient->t_connect) zclient->t_connect =
 6                 thread_add_event(zclient->master, zclient_connect, zclient, 0);
 7         break;
 8         ......
 9     }
10 }

2.2 在zclient_connect里調用zclient_socket完成客戶端sock的初始化:

1 int zclient_socket_connect(struct zclient *zclient) {
2 #ifdef HAVE_TCP_ZEBRA
3     zclient->sock = zclient_socket();
4 #else
5     zclient->sock = zclient_socket_un(zclient_serv_path_get());
6 #endif
7     return zclient->sock;
8 }
 1 static int
 2 zclient_socket(void) { 
 3     int sock;
 4     int ret;
 5     struct sockaddr_in serv;
 6 
 7     /* We should think about IPv6 connection. */
 8     sock = socket(AF_INET, SOCK_STREAM, 0);
 9     if (sock < 0) return -1;
10 
11     /* Make server socket. */
12     memset(&serv, 0, sizeof(struct sockaddr_in));
13     serv.sin_family = AF_INET;
14     serv.sin_port = htons(ZEBRA_PORT);
15 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
16     serv.sin_len = sizeof(struct sockaddr_in);
17 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
18     serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
19 
20     /* Connect to zebra. */
21     ret = connect(sock, (struct sockaddr *)&serv, sizeof(serv));
22     if (ret < 0) {
23         close(sock);
24         return -1;
25     }
26     return sock;
27 }

嗯,服務端和客戶端就這樣完成了tcp通信連接,其他的客戶端(如bgpd,ospfd等等)都是調用zclient_socket完成連接,代碼復用了哦!

3、下面來看看作為大內總管,zebra如何處理日常事務:

首先是在thread的調度中,增加了read事件(可以看到整個系統,都是由thread模塊在在暗中維持的運轉)

1 thread_add_read(zebrad.master, zebra_client_read, client, sock);
 1 /* Handler of zebra service request. */
 2 static int
 3 zebra_client_read(struct thread *thread) {
 4     ......
 5     command = stream_getw(client->ibuf);
 6     
 7     .....
 8     
 9     switch (command) {
10     .....
11     
12     case ZEBRA_IPV4_ROUTE_ADD:
13         zread_ipv4_add(client, length, vrf_id);
14         break;
15     case ZEBRA_IPV4_ROUTE_DELETE:
16         zread_ipv4_delete(client, length, vrf_id);
17         break;
18     ......
19     default:
20         zlog_info("Zebra received unknown command %d", command);
21         break;
22     }
23 
24     ......
25     
26     zebra_event(ZEBRA_READ, sock, client);
27     return 0;
28 }

如上述代碼,從消息內容中讀取到對應事件號,比如ZEBRA_IPV4_ROUTE_ADD,ZEBRA_IPV4_ROUTE_DELETE,即是增加ipv4路由和刪除ipv4路由。

4、再來看看大內總管(zebra)如何與皇上(內核)交互的:

在main里對這個過程做了初始化,函數是rib_init。

1 /* Routing information base initialize. */
2 void
3 rib_init(void)
4 {
5     rib_queue_init(&zebrad);
6 }
7 
8     /* fill in the work queue spec */
9     zebra->ribq->spec.workfunc = &meta_queue_process;

上面代碼創建一個工作隊列,作為thread調度模塊的一個低等級的后台調度(THREAD_BACKGROUND)執行的任務。在meta_queue_process函數里處理各個子隊列:

 1 /* Dispatch the meta queue by picking, processing and unlocking the next RN from
 2  * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data
 3  * is pointed to the meta queue structure.
 4  */
 5 static wq_item_status
 6 meta_queue_process(struct work_queue *dummy, void *data)
 7 {
 8     struct meta_queue *mq = data;
 9     unsigned i;
10 
11     for (i = 0; i < MQ_SIZE; i++) if (process_subq(mq->subq[i], i))
12         {
13             mq->size--;
14             break;
15         }
16     return mq->size ? WQ_REQUEUE : WQ_SUCCESS;
17 }

process_subq函數里調用rib_process函數,即開始了對路由信息的處理,整個內核的路由的新舊比較與更新:

 1 int
 2 kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new)
 3 {
 4   int route = 0;
 5 
 6   if (zserv_privs.change(ZPRIVS_RAISE))
 7     zlog (NULL, LOG_ERR, "Can't raise privileges");
 8 
 9   if (old)
10     route |= kernel_rtm (RTM_DELETE, p, old);
11 
12   if (new)
13     route |= kernel_rtm (RTM_ADD, p, new);
14 
15   if (zserv_privs.change(ZPRIVS_LOWER))
16     zlog (NULL, LOG_ERR, "Can't lower privileges");
17 
18   return route;
19 }

可以看到,最后使用netlink通信來更新內核的路由信息。

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM