mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-11 15:18:06 +00:00
doc: Update documentation
Signed-off-by: Donald Lee <dlqs@gmx.com>
This commit is contained in:
parent
64d457d7ac
commit
aed6f883a0
@ -14,8 +14,8 @@ is implemented using the standard Lua C bindings. The supported version of Lua
|
|||||||
is 5.3.
|
is 5.3.
|
||||||
|
|
||||||
C objects may be passed into Lua and Lua objects may be retrieved by C code via
|
C objects may be passed into Lua and Lua objects may be retrieved by C code via
|
||||||
a marshalling system. In this way, arbitrary data from FRR may be passed to
|
a encoding/decoding system. In this way, arbitrary data from FRR may be passed to
|
||||||
scripts. It is possible to pass C functions as well.
|
scripts.
|
||||||
|
|
||||||
The Lua environment is isolated from the C environment; user scripts cannot
|
The Lua environment is isolated from the C environment; user scripts cannot
|
||||||
access FRR's address space unless explicitly allowed by FRR.
|
access FRR's address space unless explicitly allowed by FRR.
|
||||||
@ -53,150 +53,290 @@ Reasons against supporting multiple scripting languages:
|
|||||||
with which a given script can be shared
|
with which a given script can be shared
|
||||||
|
|
||||||
General
|
General
|
||||||
^^^^^^^
|
-------
|
||||||
|
|
||||||
FRR's concept of a script is somewhat abstracted away from the fact that it is
|
FRR's scripting functionality is provided in the form of Lua functions in Lua
|
||||||
Lua underneath. A script in has two things:
|
scripts (``.lua`` files). One Lua script may contain many Lua functions. These
|
||||||
|
are respectively encapsulated in the following structures:
|
||||||
- name
|
|
||||||
- state
|
|
||||||
|
|
||||||
In code:
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
struct frrscript {
|
struct frrscript {
|
||||||
/* Script name */
|
/* Lua file name */
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
/* Lua state */
|
/* hash of lua_function_states */
|
||||||
struct lua_State *L;
|
struct hash *lua_function_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lua_function_state {
|
||||||
|
/* Lua function name */
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
lua_State *L;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
``name`` is simply a string. Everything else is in ``state``, which is itself a
|
`struct frrscript`: Since all Lua functions are contained within scripts, the
|
||||||
Lua library object (``lua_State``). This is an opaque struct that is
|
following APIs manipulates this structure. ``name`` contains the
|
||||||
manipulated using ``lua_*`` functions. The basic ones are imported from
|
Lua script name and a hash of Lua functions to their function names.
|
||||||
``lua.h`` and the rest are implemented within FRR to fill our use cases. The
|
|
||||||
thing to remember is that all operations beyond the initial loading the script
|
|
||||||
take place on this opaque state object.
|
|
||||||
|
|
||||||
There are four basic actions that can be done on a script:
|
`struct lua_function_state` is an internal structure, but it essentially contains
|
||||||
|
the name of the Lua function and its state (a stack), which is run using Lua
|
||||||
|
library functions.
|
||||||
|
|
||||||
- load
|
In general, to run a Lua function, these steps must take place:
|
||||||
- execute
|
|
||||||
- query state
|
|
||||||
- unload
|
|
||||||
|
|
||||||
They are typically done in this order.
|
- Initialization
|
||||||
|
- Load
|
||||||
|
- Call
|
||||||
|
- Delete
|
||||||
|
|
||||||
|
Initialization
|
||||||
Loading
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
A snippet of Lua code is referred to as a "chunk". These are simply text. FRR
|
|
||||||
presently assumes chunks are located in individual files specific to one task.
|
|
||||||
These files are stored in the scripts directory and must end in ``.lua``.
|
|
||||||
|
|
||||||
A script object is created by loading a script. This is done with
|
|
||||||
``frrscript_load()``. This function takes the name of the script and an
|
|
||||||
optional callback function. The string ".lua" is appended to the script name,
|
|
||||||
and the resultant filename is looked for in the scripts directory.
|
|
||||||
|
|
||||||
For example, to load ``/etc/frr/scripts/bingus.lua``:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
struct frrscript *fs = frrscript_load("bingus", NULL);
|
|
||||||
|
|
||||||
During loading the script is validated for syntax and its initial environment
|
|
||||||
is setup. By default this does not include the Lua standard library; there are
|
|
||||||
security issues to consider, though for practical purposes untrusted users
|
|
||||||
should not be able to write the scripts directory anyway. If desired the Lua
|
|
||||||
standard library may be added to the script environment using
|
|
||||||
``luaL_openlibs(fs->L)`` after loading the script. Further information on
|
|
||||||
setting up the script environment is in the Lua manual.
|
|
||||||
|
|
||||||
|
|
||||||
Executing
|
|
||||||
^^^^^^^^^
|
|
||||||
|
|
||||||
After loading, scripts may be executed. A script may take input in the form of
|
|
||||||
variable bindings set in its environment prior to being run, and may provide
|
|
||||||
results by setting the value of variables. Arbitrary C values may be
|
|
||||||
transferred into the script environment, including functions.
|
|
||||||
|
|
||||||
A typical execution call looks something like this:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
struct frrscript *fs = frrscript_load(...);
|
|
||||||
|
|
||||||
int status_ok = 0, status_fail = 1;
|
|
||||||
struct prefix p = ...;
|
|
||||||
|
|
||||||
int result = frrscript_call(fs,
|
|
||||||
("STATUS_FAIL", &status_fail),
|
|
||||||
("STATUS_OK", &status_ok),
|
|
||||||
("prefix", &p));
|
|
||||||
|
|
||||||
|
|
||||||
To execute a loaded script, we need to define the inputs. These inputs are
|
|
||||||
passed in by binding values to variable names that will be accessible within the
|
|
||||||
Lua environment. Basically, all communication with the script takes place via
|
|
||||||
global variables within the script, and to provide inputs we predefine globals
|
|
||||||
before the script runs. This is done by passing ``frrscript_call()`` a list of
|
|
||||||
parenthesized pairs, where the first and second fields identify, respectively,
|
|
||||||
the name of the global variable within the script environment and the value it
|
|
||||||
is bound to.
|
|
||||||
|
|
||||||
The script is then executed and returns a general status code. In the success
|
|
||||||
case this will be 0, otherwise it will be nonzero. The script itself does not
|
|
||||||
determine this code, it is provided by the Lua interpreter.
|
|
||||||
|
|
||||||
|
|
||||||
Querying State
|
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. todo::
|
The ``frrscript`` object encapsulates the Lua function state(s) from
|
||||||
|
one Lua script file. To create, use ``frrscript_new()`` which takes the
|
||||||
|
name of the Lua script.
|
||||||
|
The string ".lua" is appended to the script name, and the resultant filename
|
||||||
|
will be used to look for the script when we want to load a Lua function from it.
|
||||||
|
|
||||||
This section will be updated once ``frrscript_get_result`` has been
|
For example, to create ``frrscript`` for ``/etc/frr/scripts/bingus.lua``:
|
||||||
updated to work with the new ``frrscript_call`` and the rest of the new API.
|
|
||||||
|
|
||||||
|
|
||||||
Unloading
|
|
||||||
^^^^^^^^^
|
|
||||||
|
|
||||||
To destroy a script and its associated state:
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
frrscript_unload(fs);
|
struct frrscript *fs = frrscript_new("bingus");
|
||||||
|
|
||||||
|
|
||||||
.. _marshalling:
|
The script is *not* read at this stage.
|
||||||
|
This function cannot be used to test for a script's presence.
|
||||||
|
|
||||||
Marshalling
|
Load
|
||||||
^^^^^^^^^^^
|
^^^^
|
||||||
|
|
||||||
|
The function to be called must first be loaded. Use ``frrscript_load()``
|
||||||
|
which takes a ``frrscript`` object, the name of the Lua function
|
||||||
|
and a callback function.
|
||||||
|
|
||||||
|
For example, to load the Lua function ``on_foo``
|
||||||
|
in ``/etc/frr/scripts/bingus.lua``:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
int ret = frrscript_load(fs, "on_foo", NULL);
|
||||||
|
|
||||||
|
|
||||||
|
This function returns 0 if and only if the Lua function was successfully loaded.
|
||||||
|
A non-zero return could indicate either a missing Lua script, a missing
|
||||||
|
Lua function, or an error when loading the function.
|
||||||
|
|
||||||
|
During loading the script is validated for syntax and its environment
|
||||||
|
is set up. By default this does not include the Lua standard library; there are
|
||||||
|
security issues to consider, though for practical purposes untrusted users
|
||||||
|
should not be able to write the scripts directory anyway.
|
||||||
|
|
||||||
|
Call
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
After loading, Lua functions may be called.
|
||||||
|
|
||||||
|
Input
|
||||||
|
"""""
|
||||||
|
|
||||||
|
Inputs to the Lua script should be given by providing a list of parenthesized
|
||||||
|
pairs,
|
||||||
|
where the first and second field identify the name of the variable and the
|
||||||
|
value it is bound to, respectively.
|
||||||
|
The types of the values must have registered encoders (more below); the compiler
|
||||||
|
will warn you otherwise.
|
||||||
|
|
||||||
|
These variables are first encoded in-order, then provided as arguments
|
||||||
|
to the Lua function. In the example, note that ``c`` is passed in as a value
|
||||||
|
while ``a`` and ``b`` are passed in as pointers.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
int a = 100, b = 200, c = 300;
|
||||||
|
frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c));
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
function on_foo(a, b, c)
|
||||||
|
-- a is 100, b is 200, c is 300
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Output
|
||||||
|
""""""
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
int a = 100, b = 200, c = 300;
|
||||||
|
frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c));
|
||||||
|
// a is 500, b is 200, c is 300
|
||||||
|
|
||||||
|
int* d = frrscript_get_result(fs, "on_foo", "d", lua_tointegerp);
|
||||||
|
// d is 800
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
function on_foo(a, b, c)
|
||||||
|
b = 600
|
||||||
|
return { ["a"] = 500, ["c"] = 700, ["d"] = 800 }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
**Lua functions being called must return a single table of string names to
|
||||||
|
values.**
|
||||||
|
(Lua functions should return an empty table if there is no output.)
|
||||||
|
The keys of the table are mapped back to names of variables in C. Note that
|
||||||
|
the values in the table can also be tables. Since tables are Lua's primary
|
||||||
|
data structure, this design lets us return any Lua value.
|
||||||
|
|
||||||
|
After the Lua function returns, the names of variables to ``frrscript_call()``
|
||||||
|
are matched against keys of the returned table, and then decoded. The types
|
||||||
|
being decoded must have registered decoders (more below); the compiler will
|
||||||
|
warn you otherwise.
|
||||||
|
|
||||||
|
In the example, since ``a`` was in the returned table and ``b`` was not,
|
||||||
|
``a`` was decoded and its value modified, while ``b`` was not decoded.
|
||||||
|
``c`` was decoded as well, but its decoder is a noop.
|
||||||
|
What modifications happen given a variable depends whether its name was
|
||||||
|
in the returned table and the decoder's implementation.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Always keep in mind that non const-qualified pointers in
|
||||||
|
``frrscript_call()`` may be modified - this may be a source of bugs.
|
||||||
|
On the other hand, const-qualified pointers and other values cannot
|
||||||
|
be modified.
|
||||||
|
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
You can make a copy of a data structure and pass that in instead,
|
||||||
|
so that modifications only happen to that copy.
|
||||||
|
|
||||||
|
``frrscript_call()`` returns 0 if and only if the Lua function was successfully
|
||||||
|
called. A non-zero return could indicate either a missing Lua script, a missing
|
||||||
|
Lua function, or an error from the Lua interpreter.
|
||||||
|
|
||||||
|
In the above example, ``d`` was not an input to ``frrscript_call()``, so its
|
||||||
|
value must be explicitly retrieved with ``frrscript_get_result``.
|
||||||
|
|
||||||
|
``frrscript_get_result()`` takes a
|
||||||
|
decoder and string name which is used as a key to search the returned table.
|
||||||
|
Returns the pointer to the decoded value, or NULL if it was not found.
|
||||||
|
In the example, ``d`` is a "new" value in C space,
|
||||||
|
so memory allocation might take place. Hence the caller is
|
||||||
|
responsible for memory deallocation.
|
||||||
|
|
||||||
|
|
||||||
|
Delete
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
To delete a script and the all Lua states associated with it:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
frrscript_delete(fs);
|
||||||
|
|
||||||
|
|
||||||
|
A complete example
|
||||||
|
""""""""""""""""""
|
||||||
|
|
||||||
|
So, a typical exection call, with error checking, looks something like this:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
struct frrscript *fs = frrscript_new("my_script"); // name *without* .lua
|
||||||
|
|
||||||
|
int ret = frrscript_load(fs, "on_foo", NULL);
|
||||||
|
if (ret != 0)
|
||||||
|
goto DONE; // Lua script or function might have not been found
|
||||||
|
|
||||||
|
int a = 100, b = 200, c = 300;
|
||||||
|
ret = frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c));
|
||||||
|
if (ret != 0)
|
||||||
|
goto DONE; // Lua function might have not successfully run
|
||||||
|
|
||||||
|
// a and b might be modified
|
||||||
|
assert(a == 500);
|
||||||
|
assert(b == 200);
|
||||||
|
|
||||||
|
// c could not have been modified
|
||||||
|
assert(c == 300);
|
||||||
|
|
||||||
|
// d is new
|
||||||
|
int* d = frrscript_get_result(fs, "on_foo", "d", lua_tointegerp);
|
||||||
|
|
||||||
|
if (!d)
|
||||||
|
goto DONE; // "d" might not have been in returned table
|
||||||
|
|
||||||
|
assert(*d == 800);
|
||||||
|
XFREE(MTYPE_TMP, d); // caller responsible for free
|
||||||
|
|
||||||
|
DONE:
|
||||||
|
frrscript_delete(fs);
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
function on_foo(a, b, c)
|
||||||
|
b = 600
|
||||||
|
return { a = 500, c = 700, d = 800 }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Note that ``{ a = ...`` is same as ``{ ["a"] = ...``; it is Lua shorthand to
|
||||||
|
use the variable name as the key in a table.
|
||||||
|
|
||||||
|
Encoding and Decoding
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Earlier sections glossed over the types of values that can be passed into
|
Earlier sections glossed over the types of values that can be passed into
|
||||||
``frrscript_call`` and how data is passed between C and Lua. Lua, as a dynamically
|
``frrscript_call()`` and how data is passed between C and Lua. Lua, as a
|
||||||
typed, garbage collected language, cannot directly use C values without some
|
dynamically typed, garbage collected language, cannot directly use C values
|
||||||
kind of marshalling / unmarshalling system to translate types between the two
|
without some kind of encoding / decoding system to
|
||||||
runtimes.
|
translate types between the two runtimes.
|
||||||
|
|
||||||
Lua communicates with C code using a stack. C code wishing to provide data to
|
Lua communicates with C code using a stack. C code wishing to provide data to
|
||||||
Lua scripts must provide a function that marshalls the C data into a Lua
|
Lua scripts must provide a function that encodes the C data into a Lua
|
||||||
representation and pushes it on the stack. C code wishing to retrieve data from
|
representation and pushes it on the stack. C code wishing to retrieve data from
|
||||||
Lua must provide a corresponding unmarshalling function that retrieves a Lua
|
Lua must provide a corresponding decoder function that retrieves a Lua
|
||||||
value from the stack and converts it to the corresponding C type. These
|
value from the stack and converts it to the corresponding C type.
|
||||||
functions are known as encoders and decoders in FRR.
|
|
||||||
|
|
||||||
An encoder is a function that takes a ``lua_State *`` and a C type and pushes
|
Encoders and decoders are provided for common data types.
|
||||||
onto the Lua stack a value representing the C type. For C structs, the usual
|
Developers wishing to pass their own data structures between C and Lua need to
|
||||||
case, this will typically be a Lua table (tables are the only datastructure Lua
|
create encoders and decoders for that data type.
|
||||||
has). For example, here is the encoder function for ``struct prefix``:
|
|
||||||
|
|
||||||
|
We try to keep them named consistently.
|
||||||
|
There are three kinds of encoders and decoders:
|
||||||
|
|
||||||
|
1. lua_push*: encodes a value onto the Lua stack.
|
||||||
|
Required for ``frrscript_call``.
|
||||||
|
|
||||||
|
2. lua_decode*: decodes a value from the Lua stack.
|
||||||
|
Required for ``frrscript_call``.
|
||||||
|
Only non const-qualified pointers may be actually decoded (more below).
|
||||||
|
|
||||||
|
3. lua_to*: allocates memory and decodes a value from the Lua stack.
|
||||||
|
Required for ``frrscript_get_result``.
|
||||||
|
|
||||||
|
This design allows us to combine typesafe *modification* of C values as well as
|
||||||
|
*allocation* of new C values.
|
||||||
|
|
||||||
|
In the following sections, we will use the encoders/decoders for ``struct prefix`` as an example.
|
||||||
|
|
||||||
|
Encoding
|
||||||
|
""""""""
|
||||||
|
|
||||||
|
An encoder function takes a ``lua_State *``, a C type and pushes that value onto
|
||||||
|
the Lua state (a stack).
|
||||||
|
For C structs, the usual case,
|
||||||
|
this will typically be encoded to a Lua table, then pushed onto the Lua stack.
|
||||||
|
|
||||||
|
Here is the encoder function for ``struct prefix``:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -204,8 +344,6 @@ has). For example, here is the encoder function for ``struct prefix``:
|
|||||||
{
|
{
|
||||||
char buffer[PREFIX_STRLEN];
|
char buffer[PREFIX_STRLEN];
|
||||||
|
|
||||||
zlog_debug("frrlua: pushing prefix table");
|
|
||||||
|
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
|
lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
|
||||||
lua_setfield(L, -2, "network");
|
lua_setfield(L, -2, "network");
|
||||||
@ -215,7 +353,7 @@ has). For example, here is the encoder function for ``struct prefix``:
|
|||||||
lua_setfield(L, -2, "family");
|
lua_setfield(L, -2, "family");
|
||||||
}
|
}
|
||||||
|
|
||||||
This function pushes a single value onto the Lua stack. It is a table whose
|
This function pushes a single value, a table, onto the Lua stack, whose
|
||||||
equivalent in Lua is:
|
equivalent in Lua is:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
@ -223,16 +361,23 @@ equivalent in Lua is:
|
|||||||
{ ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
|
{ ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
|
||||||
|
|
||||||
|
|
||||||
|
Decoding
|
||||||
|
""""""""
|
||||||
|
|
||||||
Decoders are a bit more involved. They do the reverse; a decoder function takes
|
Decoders are a bit more involved. They do the reverse; a decoder function takes
|
||||||
a ``lua_State *``, pops a value off the Lua stack and converts it back into its
|
a ``lua_State *``, pops a value off the Lua stack and converts it back into its
|
||||||
C type.
|
C type.
|
||||||
However, since Lua programs have the ability to directly modify their inputs
|
|
||||||
(i.e. values passed in via ``frrscript_call``), we need two separate decoder
|
|
||||||
functions, called ``lua_decode_*`` and ``lua_to*``.
|
|
||||||
|
|
||||||
A ``lua_decode_*`` function takes a ``lua_State*``, an index, and a C type, and
|
There are two: ``lua_decode*`` and ``lua_to*``. The former does no mememory
|
||||||
unmarshalls a Lua value into that C type.
|
allocation and is needed for ``frrscript_call``.
|
||||||
Again, for ``struct prefix``:
|
The latter performs allocation and is optional.
|
||||||
|
|
||||||
|
A ``lua_decode_*`` function takes a ``lua_State*``, an index, and a pointer
|
||||||
|
to a C data structure, and directly modifies the structure with values from the
|
||||||
|
Lua stack. Note that only non const-qualified pointers may be modified;
|
||||||
|
``lua_decode_*`` for other types will be noops.
|
||||||
|
|
||||||
|
Again, for ``struct prefix *``:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -240,22 +385,45 @@ Again, for ``struct prefix``:
|
|||||||
{
|
{
|
||||||
lua_getfield(L, idx, "network");
|
lua_getfield(L, idx, "network");
|
||||||
(void)str2prefix(lua_tostring(L, -1), prefix);
|
(void)str2prefix(lua_tostring(L, -1), prefix);
|
||||||
|
/* pop the netork string */
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
/* pop the table */
|
/* pop the prefix table */
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- Before ``lua_decode*`` is run, the "prefix" table is already on the top of
|
||||||
|
the stack. ``frrscript_call`` does this for us.
|
||||||
|
- However, at the end of ``lua_decode*``, the "prefix" table should be popped.
|
||||||
|
- The other two fields in the "network" table are disregarded, meaning that any
|
||||||
|
modification to them is discarded in C space. In this case, this is desired
|
||||||
|
behavior.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
``lua_decode_prefix`` functions should leave the Lua stack completely empty
|
``lua_decode*`` functions should pop all values that ``lua_to*`` pushed onto
|
||||||
when they return.
|
the Lua stack.
|
||||||
For decoders that unmarshall fields from tables, remember to pop the table
|
For encoders that pushed a table, its decoder should pop the table at the end.
|
||||||
at the end.
|
The above is an example.
|
||||||
|
|
||||||
|
|
||||||
A ``lua_to*`` function perform a similar role except that it first allocates
|
|
||||||
memory for the new C type before decoding the value from the Lua stack, then
|
``int`` is not a non const-qualified pointer, so for ``int``:
|
||||||
returns a pointer to the newly allocated C type.
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void lua_decode_int_noop(lua_State *L, int idx, int i)
|
||||||
|
{ //noop
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
A ``lua_to*`` function provides identical functionality except that it first
|
||||||
|
allocates memory for the new C type before decoding the value from the Lua stack,
|
||||||
|
then returns a pointer to the newly allocated C type. You only need to implement
|
||||||
|
this function to use with ``frrscript_get_result`` to retrieve a result of
|
||||||
|
this type.
|
||||||
|
|
||||||
This function can and should be implemented using ``lua_decode_*``:
|
This function can and should be implemented using ``lua_decode_*``:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
@ -274,18 +442,11 @@ allocated with ``MTYPE_TMP``. This way it is possible to unload the script
|
|||||||
(destroy the state) without invalidating any references to values stored in it.
|
(destroy the state) without invalidating any references to values stored in it.
|
||||||
Note that it is the caller's responsibility to free the data.
|
Note that it is the caller's responsibility to free the data.
|
||||||
|
|
||||||
For consistency, we should always name functions of the first type
|
|
||||||
``lua_decode_*``.
|
|
||||||
Functions of the second type should be named ``lua_to*``, as this is the
|
|
||||||
naming convention used by the Lua C library for the basic types e.g.
|
|
||||||
``lua_tointeger`` and ``lua_tostring``.
|
|
||||||
|
|
||||||
This two-function design allows the compiler to warn if a value passed into
|
Registering encoders and decoders for frrscript_call
|
||||||
``frrscript_call`` does not have a encoder and decoder for that type.
|
""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
The ``lua_to*`` functions enable us to easily create decoders for nested
|
|
||||||
structures.
|
|
||||||
|
|
||||||
To register a new type with its corresponding encoding and decoding functions,
|
To register a new type with its ``lua_push*`` and ``lua_decode*`` functions,
|
||||||
add the mapping in the following macros in ``frrscript.h``:
|
add the mapping in the following macros in ``frrscript.h``:
|
||||||
|
|
||||||
.. code-block:: diff
|
.. code-block:: diff
|
||||||
@ -331,11 +492,12 @@ For that, use ``lua_decode_noop``:
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Marshalled types are not restricted to simple values like integers, strings
|
Encodable/decodable types are not restricted to simple values like integers,
|
||||||
and tables. It is possible to marshall a type such that the resultant object
|
strings and tables.
|
||||||
in Lua is an actual object-oriented object, complete with methods that call
|
It is possible to encode a type such that the resultant object in Lua
|
||||||
back into defined C functions. See the Lua manual for how to do this; for a
|
is an actual object-oriented object, complete with methods that call
|
||||||
code example, look at how zlog is exported into the script environment.
|
back into defined C functions. See the Lua manual for how to do this;
|
||||||
|
for a code example, look at how zlog is exported into the script environment.
|
||||||
|
|
||||||
|
|
||||||
Script Environment
|
Script Environment
|
||||||
@ -364,10 +526,11 @@ Examples
|
|||||||
For a complete code example involving passing custom types, retrieving results,
|
For a complete code example involving passing custom types, retrieving results,
|
||||||
and doing complex calculations in Lua, look at the implementation of the
|
and doing complex calculations in Lua, look at the implementation of the
|
||||||
``match script SCRIPT`` command for BGP routemaps. This example calls into a
|
``match script SCRIPT`` command for BGP routemaps. This example calls into a
|
||||||
script with a route prefix and attributes received from a peer and expects the
|
script with a function named ``route_match``,
|
||||||
script to return a match / no match / match and update result.
|
provides route prefix and attributes received from a peer and expects the
|
||||||
|
function to return a match / no match / match and update result.
|
||||||
|
|
||||||
An example script to use with this follows. This script matches, does not match
|
An example script to use with this follows. This function matches, does not match
|
||||||
or updates a route depending on how many BGP UPDATE messages the peer has
|
or updates a route depending on how many BGP UPDATE messages the peer has
|
||||||
received when the script is called, simply as a demonstration of what can be
|
received when the script is called, simply as a demonstration of what can be
|
||||||
accomplished with scripting.
|
accomplished with scripting.
|
||||||
@ -378,64 +541,75 @@ accomplished with scripting.
|
|||||||
-- Example route map matching
|
-- Example route map matching
|
||||||
-- author: qlyoung
|
-- author: qlyoung
|
||||||
--
|
--
|
||||||
-- The following variables are available to us:
|
-- The following variables are available in the global environment:
|
||||||
-- log
|
-- log
|
||||||
-- logging library, with the usual functions
|
-- logging library, with the usual functions
|
||||||
-- prefix
|
--
|
||||||
|
-- route_match arguments:
|
||||||
|
-- table prefix
|
||||||
-- the route under consideration
|
-- the route under consideration
|
||||||
-- attributes
|
-- table attributes
|
||||||
-- the route's attributes
|
-- the route's attributes
|
||||||
-- peer
|
-- table peer
|
||||||
-- the peer which received this route
|
-- the peer which received this route
|
||||||
-- RM_FAILURE
|
-- integer RM_FAILURE
|
||||||
-- status code in case of failure
|
-- status code in case of failure
|
||||||
-- RM_NOMATCH
|
-- integer RM_NOMATCH
|
||||||
-- status code for no match
|
-- status code for no match
|
||||||
-- RM_MATCH
|
-- integer RM_MATCH
|
||||||
-- status code for match
|
-- status code for match
|
||||||
-- RM_MATCH_AND_CHANGE
|
-- integer RM_MATCH_AND_CHANGE
|
||||||
-- status code for match-and-set
|
-- status code for match-and-set
|
||||||
--
|
--
|
||||||
-- We need to set the following out values:
|
-- route_match returns table with following keys:
|
||||||
-- action
|
-- integer action, required
|
||||||
-- Set to the appropriate status code to indicate what we did
|
-- resultant status code. Should be one of RM_*
|
||||||
-- attributes
|
-- table attributes, optional
|
||||||
-- Setting fields on here will propagate them back up to the caller if
|
-- updated route attributes
|
||||||
-- 'action' is set to RM_MATCH_AND_CHANGE.
|
--
|
||||||
|
|
||||||
|
function route_match(prefix, attributes, peer,
|
||||||
|
RM_FAILURE, RM_NOMATCH, RM_MATCH, RM_MATCH_AND_CHANGE)
|
||||||
|
|
||||||
|
log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string)
|
||||||
|
|
||||||
|
function on_match (prefix, attributes)
|
||||||
|
log.info("Match")
|
||||||
|
return {
|
||||||
|
attributes = RM_MATCH
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_nomatch (prefix, attributes)
|
||||||
|
log.info("No match")
|
||||||
|
return {
|
||||||
|
action = RM_NOMATCH
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_match_and_change (prefix, attributes)
|
||||||
|
log.info("Match and change")
|
||||||
|
attributes["metric"] = attributes["metric"] + 7
|
||||||
|
return {
|
||||||
|
action = RM_MATCH_AND_CHANGE,
|
||||||
|
attributes = attributes
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
special_routes = {
|
||||||
|
["172.16.10.4/24"] = on_match,
|
||||||
|
["172.16.13.1/8"] = on_nomatch,
|
||||||
|
["192.168.0.24/8"] = on_match_and_change,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string)
|
if special_routes[prefix.network] then
|
||||||
|
return special_routes[prefix.network](prefix, attributes)
|
||||||
function on_match (prefix, attrs)
|
elseif peer.stats.update_in % 3 == 0 then
|
||||||
log.info("Match")
|
return on_match(prefix, attributes)
|
||||||
action = RM_MATCH
|
elseif peer.stats.update_in % 2 == 0 then
|
||||||
end
|
return on_nomatch(prefix, attributes)
|
||||||
|
else
|
||||||
function on_nomatch (prefix, attrs)
|
return on_match_and_change(prefix, attributes)
|
||||||
log.info("No match")
|
end
|
||||||
action = RM_NOMATCH
|
end
|
||||||
end
|
|
||||||
|
|
||||||
function on_match_and_change (prefix, attrs)
|
|
||||||
action = RM_MATCH_AND_CHANGE
|
|
||||||
log.info("Match and change")
|
|
||||||
attrs["metric"] = attrs["metric"] + 7
|
|
||||||
end
|
|
||||||
|
|
||||||
special_routes = {
|
|
||||||
["172.16.10.4/24"] = on_match,
|
|
||||||
["172.16.13.1/8"] = on_nomatch,
|
|
||||||
["192.168.0.24/8"] = on_match_and_change,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if special_routes[prefix.network] then
|
|
||||||
special_routes[prefix.network](prefix, attributes)
|
|
||||||
elseif peer.stats.update_in % 3 == 0 then
|
|
||||||
on_match(prefix, attributes)
|
|
||||||
elseif peer.stats.update_in % 2 == 0 then
|
|
||||||
on_nomatch(prefix, attributes)
|
|
||||||
else
|
|
||||||
on_match_and_change(prefix, attributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user