zoukankan      html  css  js  c++  java
  • 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通信来更新内核的路由信息。

      

  • 相关阅读:
    Java对象序列化/反序列化的注意事项
    初尝Java序列化/反序列化对象
    Java的byte数组
    在C#或者SWT上跨进程访问SWT控件的问题
    Enum实现单例模式
    看个泛型的例子
    构造器优化需要多个构造器的类(建造者模式)
    线程间操作无效: 从不是创建控件的线程访问它.
    SWT中的多线程(Invalid thread access)
    读书笔记 --腾讯传
  • 原文地址:https://www.cnblogs.com/danxi/p/6275320.html
Copyright © 2011-2022 走看看