Skip to content

Commit

Permalink
zebra: add script wrapper framework to interact with script
Browse files Browse the repository at this point in the history
This framework has 2 APIs:
- one that is able to execute a script command
- the other that is able to execute a script command, and return the
  output in a json structure
This framework permits gaining time, since it allows frr to call some
external programs and rely on externals like the output.

Signed-off-by: Philippe Guibert <[email protected]>
  • Loading branch information
pguibert6WIND committed Apr 5, 2018
1 parent 0c842c4 commit 372764e
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 0 deletions.
2 changes: 2 additions & 0 deletions zebra/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/label_manager.h"
#include "zebra/zebra_netns_notify.h"
#include "zebra/zebra_wrap_script.h"

#define ZEBRA_PTM_SUPPORT

Expand Down Expand Up @@ -306,6 +307,7 @@ int main(int argc, char **argv)
zebrad.master = frr_init();

/* Zebra related initialize. */
zebra_wrap_init();
zserv_init();
rib_init();
zebra_if_init();
Expand Down
2 changes: 2 additions & 0 deletions zebra/subdir.am
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ zebra_zebra_SOURCES = \
zebra/zebra_netns_id.c \
zebra/zebra_netns_notify.c \
zebra/table_manager.c \
zebra/zebra_wrap_script.c \
# end

zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS)
Expand Down Expand Up @@ -115,6 +116,7 @@ noinst_HEADERS += \
zebra/zebra_netns_id.h \
zebra/zebra_netns_notify.h \
zebra/table_manager.h \
zebra/zebra_wrap_script.h \
# end

zebra_zebra_irdp_la_SOURCES = \
Expand Down
289 changes: 289 additions & 0 deletions zebra/zebra_wrap_script.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
/*
* Zebra Script Wrapper
* Copyright (C) 2018 6WIND
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FRR; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/

#include <zebra.h>

#include "json.h"

#include "zebra/debug.h"
#include "zebra/zebra_wrap_script.h"

/* this struct temporarily stores the list of headers
* - name is used to store the name of the header field
* - attribute can be used to store values that have no column
* assigned ( extra param)
* ex:
* columnA columnB columnC columnD
* <val1> <val2> <val3> <val4> <val5> <val6>..
* <val7> <val8> <val9> <val10> <val11> <val12>..
* columnA .. columnD are stored in name field
* attribute us used to store <val5>..<val6> or <val11>..<val12>
*/
struct item_list {
char *name;
char *attribute;
};

#define DATA_LEN 4096
#define ITEM_MAXIMUM 15
#define DATA_LINE_MAX 200

/* debug information
*/
#define SCRIPT_DEBUG (1<<1)
#define SCRIPT_ITEM_LIST (1<<2)
#define SCRIPT_ELEMENT_LIST (1<<3)

static int zebra_wrap_debug;


void zebra_wrap_init(void)
{
zebra_wrap_debug = 0;
}

static int search_current_word(char *current_str, int init,
char current_word[])
{
bool word_began = false;
char *ptr_word;
int k, l = 0, m = 0;

for (k = init; k < (int)strlen(current_str); k++) {
m ++;
/* a word is made up of a char
* or a digit
* or a character between '!' and '/'
*/
if (word_began == false &&
(isalpha(current_str[k])
|| isdigit(current_str[k])
|| (current_str[k] > 0x21
&& current_str[k] < 0x2f))) {
ptr_word = &(current_str[k]);
word_began = true;
l = 0;
}
if (word_began == false)
continue;
if (!isspace(current_str[k])) {
l += 1;
continue;
}
memcpy(current_word, ptr_word, (size_t)l);
current_word[l] = '\0';
return m;
}
if (word_began)
return m;
return -1;
}

static int handle_field_line(struct json_object *json_obj,
char *current_str,
struct item_list item[])
{
int k, l = 0;
char current_word[DATA_LINE_MAX];
int nb_items = 0;
bool keep_item = false;

/* get headers from current_str */
for (k = 0; k < (int)strlen(current_str);) {
l = search_current_word(current_str, k,
current_word);
if (l < 0)
break;
k += l;
/* no json obj. fields are filled in
*/
if (json_obj == NULL) {
if (zebra_wrap_debug
& SCRIPT_ITEM_LIST)
zlog_err("SCRIPT: (%d)ITEM %s",
nb_items, current_word);
item[nb_items].name =
XSTRDUP(MTYPE_TMP, current_word);
} else {
/* if a field has no column, create "misc"
* column
*/
if (!item[nb_items].name) {
item[nb_items].name =
XSTRDUP(MTYPE_TMP, "misc");
keep_item = true;
item[nb_items].attribute =
XSTRDUP(MTYPE_TMP,
current_word);
} else if (item[nb_items].attribute) {
/* store last elements in attribute
*/
char temp_word[DATA_LINE_MAX];

snprintf(temp_word,
DATA_LINE_MAX,
"%s %s",
item[nb_items].attribute,
current_word);
XFREE(MTYPE_TMP,
item[nb_items].attribute);
item[nb_items].attribute =
XSTRDUP(MTYPE_TMP,
temp_word);
}
if (!keep_item) {
json_object_string_add(json_obj,
item[nb_items].name,
current_word);
if (zebra_wrap_debug
& SCRIPT_ELEMENT_LIST)
zlog_err("(%d)ITEM Obtained "
"for %s is %s",
nb_items,
item[nb_items].name,
current_word);
}
}
if (!keep_item)
nb_items++;
if (nb_items >= ITEM_MAXIMUM) {
int m;

for (m = 0; m < ITEM_MAXIMUM; m++)
XFREE(MTYPE_TMP, item[m].name);
if (json_obj)
json_object_free(json_obj);
return -1;
}
}
/* store last attribute to json
*/
if (keep_item) {
json_object_string_add(json_obj,
item[nb_items].name,
item[nb_items].attribute);
if (zebra_wrap_debug & SCRIPT_ITEM_LIST)
zlog_err("(%d)ITEM Obtained for %s is %s",
nb_items,item[nb_items].name,
item[nb_items].attribute);
XFREE(MTYPE_TMP, item[nb_items].attribute);
item[nb_items].attribute = NULL;
XFREE(MTYPE_TMP, item[nb_items].name);
item[nb_items].name = NULL;
}
return 0;
}

/* script : command line to execute in a shell script
* return_data : set to true if want to get back some information
* begin_at_line : the line number where to begin parsing headers and other
* - ex: following dump example begins at line 2, where header is located
* # iptables -t mangle -L PREROUTING -v
* Chain PREROUTING (policy ACCEPT 150k packets, 7426 bytes) (## line 0)
* pkts bytes target prot opt in out source destination (## line 1)
* 0 0 DROP all -- any any anywhere anywhere
* match-set match0x55f44 (## line 2)
* json_obj_list : the json structure mapped to the output, ranked with line nb
* - ex: above dump gives following
* { "2":{"pkts":"0","bytes":"0","target":"MARK","prot":"all", \
* "opt":"--","in":"any",..}}
*/

int zebra_wrap_script(const char *script, bool return_data,
int begin_at_line, struct json_object *json_obj_list)
{
FILE *fp;
char data[DATA_LEN];
char *current_str = NULL;
int nb_entries = 0;
int line_nb = 0, i;
struct item_list item[ITEM_MAXIMUM];

/* initialise item list
*/
for (i = 0; i < ITEM_MAXIMUM; i++)
memset(&item[i], 0, sizeof(struct item_list));
if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND)
zlog_debug("SCRIPT : %s", script);
fp = popen(script, "r");
if (!fp) {
zlog_err("NETLINK: error calling %s", script);
return -1;
}
if (!return_data) {
if (pclose(fp))
zlog_err("SCRIPT: error with %s: closing stream",
script);
return -1;
}
do {
json_object *json_obj = NULL;

memset(data, 0, DATA_LEN);
current_str = fgets(data, DATA_LEN, fp);
if (current_str) {
/* data contains the line
*/
current_str = data;
if (zebra_wrap_debug & SCRIPT_DEBUG)
zlog_debug("SCRIPT : [%d/%d] %s",
line_nb,
(int)strlen(current_str),
current_str);
if ((strlen(current_str) <= 1) ||
line_nb < begin_at_line) {
line_nb++;
continue;
}
if (line_nb > begin_at_line)
json_obj = json_object_new_object();
else
json_obj = NULL;
if (handle_field_line(json_obj, current_str,
item) < 0)
return -1;
if (json_obj) {
char line[10];

snprintf(line, sizeof(line), "%d", nb_entries);
json_object_object_add(json_obj_list, line, json_obj);
nb_entries++;
}
line_nb++;
}
} while (current_str != NULL);
/* free item list
*/
for (i = 0; i < ITEM_MAXIMUM; i++) {
if (item[i].name) {
XFREE(MTYPE_TMP, item[i].name);
item[i].name = NULL;
}
}
if (pclose(fp))
zlog_err("NETLINK: error closing stream with %s", script);
return 0;
}

int zebra_wrap_script_call_only(const char *script)
{
zebra_wrap_script(script, false, 0, NULL);
return 0;
}
34 changes: 34 additions & 0 deletions zebra/zebra_wrap_script.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Zebra Wrap Script Definitions
* Copyright (C) 2018 6WIND
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FRR; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/

#ifndef _ZEBRA_WRAP_SCRIPT_H
#define _ZEBRA_WRAP_SCRIPT_H

#include <zebra.h>

struct json_object;
extern int zebra_wrap_script(const char *script, bool return_data,
int begin_at_line, struct json_object *json);

extern int zebra_wrap_script_call_only(const char *script);

extern void zebra_wrap_init(void);

#endif /* _ZEBRA_WRAP_SCRIPT_H */

0 comments on commit 372764e

Please sign in to comment.