发布于

创建你的第一个FRRouting守护进程

系列: 编写FRRouting的守护进程
章节: (1/2)

我个人写c语言写的并不多,对makefile构建系统的研究不是很透彻,所以在这里我会尽量详细的介绍一些步骤,希望能够帮助到你。

前言

演示的linux发行版为Ubuntu 22.04,FRRouting版本为10.2.1。

本片文章默认你在阅读了官方安装文档,并且能够成功 安装官方10.2.1版本的FRRouting 的基础上,进行下一步操作。

由于我之前是通过docker安装并测试的FRRouting,所以在这里我会以docker的方式来进行最终效果演示。(官方的alpine docker安装教程)

编写daemon

本文的目的是为了让你能够编写一个简单的daemon,所以我会尽量简化代码,让你能够更容易的理解。

第一步-为构建做准备

github diff 示例

不妨将你的第一个守护进程命名为helloworld。就像dockerd一样,这个d代表的是daemon

本教程中其实并未使用到vtysh的功能,如下修改的文件简单介绍如下:

  • configure.ac:用于配置是否启用一些功能,即./configure相关的配置。
  • Makefile.am:用于编译构建的文件。
Configure.ac的配置
configure.ac
@@ -733,6 +733,8 @@ AC_ARG_ENABLE([babeld],
   AS_HELP_STRING([--disable-babeld], [do not build babeld]))
 AC_ARG_ENABLE([watchfrr],
   AS_HELP_STRING([--disable-watchfrr], [do not build watchfrr]))
+AC_ARG_ENABLE([helloworld],
+  AS_HELP_STRING([--disable-helloworld], [do not build helloworld]))
 AC_ARG_ENABLE([isisd],
   AS_HELP_STRING([--disable-isisd], [do not build isisd]))
 AC_ARG_ENABLE([pimd],
configure.ac
@@ -1877,6 +1879,11 @@ AS_IF([test "$enable_babeld" != "no"], [
   AC_DEFINE([HAVE_BABELD], [1], [babeld])
 ])

+## HAVE_HELLOWORLD used for vtysh/vtysh.c
+AS_IF([test "$enable_helloworld" != "no"], [
+  AC_DEFINE([HAVE_HELLOWORLD], [1], [helloworld])
+])
+
 AS_IF([test "$enable_isisd" != "no"], [
   AC_DEFINE([HAVE_ISISD], [1], [isisd])
 ])
configure.ac
@@ -2802,6 +2809,7 @@ AM_CONDITIONAL([OSPFCLIENT], [test "$OSPFCLIENT" = "ospfclient"])
 AM_CONDITIONAL([RIPNGD], [test "$enable_ripngd" != "no"])
 AM_CONDITIONAL([BABELD], [test "$enable_babeld" != "no"])
 AM_CONDITIONAL([OSPF6D], [test "$enable_ospf6d" != "no"])
+AM_CONDITIONAL([HELLOWORLD], [test "$enable_helloworld" != "no"])
 AM_CONDITIONAL([ISISD], [test "$enable_isisd" != "no"])
 AM_CONDITIONAL([PIMD], [test "$enable_pimd" != "no"])
 AM_CONDITIONAL([PIM6D], [test "$enable_pim6d" != "no"])
Makefile.am的配置
Makefile.am
@@ -194,6 +194,7 @@ include ripngd/subdir.am
 include ospfd/subdir.am
 include ospf6d/subdir.am
 include ospfclient/subdir.am
+include helloworld/subdir.am
 include isisd/subdir.am
 include nhrpd/subdir.am
 include ldpd/subdir.am
Makefile.am
@@ -273,6 +274,7 @@ EXTRA_DIST += \
        eigrpd/Makefile \
        fpm/Makefile \
        grpc/Makefile \
+       helloworld/Makefile \
        isisd/Makefile \
        ldpd/Makefile \
        lib/Makefile \
lib/libfrr.h的配置
lib/libfrr.h
@@ -103,6 +103,9 @@ DECLARE_DLIST(log_args, struct log_arg, itm);
 #define MGMTD_VTY_PORT 2623
 /* Registry of daemons' port defaults */

+/* your daemon */
+#define HELLOWORLD_VTY_PORT 2624
+
 enum frr_cli_mode {
        FRR_CLI_CLASSIC = 0,
        FRR_CLI_TRANSACTIONAL,

让frr启动项知道你的守护进程

在这之后,你需要让frr启动项知道你的守护进程

tools/etc/frr/daemons
tools/etc/frr/daemons
@@ -19,6 +19,7 @@ ospfd=no
 ospf6d=no
 ripd=no
 ripngd=no
+helloworld=yes
 isisd=no
 pimd=no
 pim6d=no
tools/etc/frr/daemons
@@ -46,6 +47,7 @@ ospfd_options="  -A 127.0.0.1"
 ospf6d_options=" -A ::1"
 ripd_options="   -A 127.0.0.1"
 ripngd_options=" -A ::1"
+helloworld_options=" -A 127.0.0.1"
 isisd_options="  -A 127.0.0.1"
 pimd_options="   -A 127.0.0.1"
 pim6d_options="  -A ::1"
tools/frrcommon.sh.in
tools/frrcommon.sh.in
@@ -36,7 +36,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
 # - keep zebra first
 # - watchfrr does NOT belong in this list

-DAEMONS="zebra mgmtd bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
+DAEMONS="zebra mgmtd bgpd ripd ripngd ospfd ospf6d helloworld isisd babeld pimd pim6d ldpd nhrpdeigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
 RELOAD_SCRIPT="$D_PATH/frr-reload.py"

 #

第二步-编写helloworld守护进程

github diff 示例

首先需要在项目目录下创建文件夹 helloworld,并在此文件夹中创建相关文件。

1. 准备构建相关文件: Makefile subdir.am .gitignore
helloworld/Makefile
all: ALWAYS
       @$(MAKE) -s -C .. helloworld/helloworld
%: ALWAYS
       @$(MAKE) -s -C .. helloworld/$@

Makefile:
       #nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:
helloworld/subdir.am
#
# helloworld
#

if HELLOWORLD
sbin_PROGRAMS += helloworld/helloworld
vtysh_daemons += helloworld

endif

helloworld_helloworld_SOURCES = \
       helloworld/helloworld_main.c \
       helloworld/helloworld.c \
       # end

noinst_HEADERS += \
       helloworld/helloworld.h \
       # end

helloworld_helloworld_LDADD = lib/libfrr.la $(LIBCAP)
helloworld/.gitignore
helloworld
2. 开始编写守护进程相关代码
helloworld/helloworld.h
#ifndef _BGPMGMTD_H
#define _BGPMGMTD_H

#include "lib/libfrr.h"

#endif /* _BGPMGMTD_H */
helloworld/helloworld.c
#include "helloworld/helloworld.h"

核心代码⬇️

helloworld/helloworld_main.c
#include <zebra.h>

#include <lib/version.h>

#include "helloworld/helloworld.h"

/* Master of threads. */
struct event_loop *master;

/* 处理中断信号 */
/* signal definitions */
void sighup(void);
void sigint(void);
void sigusr1(void);

/* SIGHUP handler. */
void sighup(void)
{
	zlog_info("SIGHUP received, ignoring");

	return;
}

/* SIGUSR1 handler. */
void sigusr1(void)
{
	zlog_rotate();
}

/* SIGINT handler. */
__attribute__((__noreturn__)) void sigint(void)
{
	zlog_notice("Terminating on signal");

  /* 执行在此hook中注册的函数,大多跟结束程序相关 */
  /* Signalize shutdown. */
	frr_early_fini();

  /* 执行在此hook中注册的函数,多跟回收资源相关 */
  /* Terminate and free() FRR related memory. */
	frr_fini();

	exit(0);
}

static struct frr_signal_t helloworld_signals[] = {
	{
		.signal = SIGHUP,
		.handler = &sighup,
	},
	{
		.signal = SIGUSR1,
		.handler = &sigusr1,
	},
	{
		.signal = SIGINT,
		.handler = &sigint,
	},
	{
		.signal = SIGTERM,
		.handler = &sigint,
	},
};

/* privileges */
static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW,
					 ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN};

struct zebra_privs_t helloworld_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
	.user = FRR_USER,
	.group = FRR_GROUP,
#endif
#ifdef VTY_GROUP
	.vty_group = VTY_GROUP,
#endif
	.caps_p = _caps_p,
	.cap_num_p = array_size(_caps_p),
	.cap_num_i = 0,
};

static struct frr_daemon_info helloworld_di;

FRR_DAEMON_INFO(helloworld, BGPMGMTD,
    .vty_port = HELLOWORLD_VTY_PORT,
    .proghelp = "Implementation of your first helloworld Daemon.",

    .signals = helloworld_signals,
    .n_signals = array_size(helloworld_signals),

    .privs = &helloworld_privs,
);

int main(int argc, char **argv)
{
    int opt;

    frr_preinit(&helloworld_di, argc, argv);

    /* 处理参数,重要不可删除 */
    while (true) {
		opt = frr_getopt(argc, argv, NULL);
		if (opt == EOF)
			break;
    }

    /* Initialize FRR infrastructure. */
	master = frr_init();

	frr_config_fork();

  /* not the recommended way to log, you should use zlog instead of fprintf */
  fprintf(stdout, "helloworld! your first deamon helloworld has started.\n");

  /* 在frr事件循环中添加此守护进程 */
	frr_run(master);

	/* Not reached. */
	return 0;
}

编译运行

可选项(主机make来查看是否有编译错误)

根据官网安装好libyang2后,执行如下命令进行构建环境的准备以及编译构建,可以不需要安装至主机

#/bin/bash

./bootstrap.sh
./configure \
    --prefix=/usr \
    --includedir=\${prefix}/include \
    --bindir=\${prefix}/bin \
    --sbindir=\${prefix}/lib/frr \
    --libdir=\${prefix}/lib/frr \
    --libexecdir=\${prefix}/lib/frr \
    --sysconfdir=/etc \
    --localstatedir=/var \
    --with-moduledir=\${prefix}/lib/frr/modules \
    --enable-configfile-mask=0640 \
    --enable-logfile-mask=0640 \
    --enable-snmp=agentx \
    --enable-multipath=64 \
    --enable-user=frr \
    --enable-group=frr \
    --enable-vty-group=frrvty \
    --with-pkg-git-version \
    --with-pkg-extra-version=-MyOwnFRRVersion
make -j$(nproc)

docker构建

官方的alpine docker安装教程

在此之前,确保你执行了这一步骤 (让frr启动项知道你的守护进程)

构建镜像
docker build -t frr-helloworld -f docker/alpine/Dockerfile .
运行容器
docker run -it --rm --name frr-helloworld \
        --privileged \
        -v ./tools/etc/frr/daemons:/etc/frr/daemons \
        frr-helloworld

运行效果

helloworld!

分享