]> git.proxmox.com Git - mirror_frr.git/blob - doc/developer/scripting.rst
Merge pull request #8278 from ckishimo/ospfv3_iface
[mirror_frr.git] / doc / developer / scripting.rst
1 .. _scripting:
2
3 Scripting
4 =========
5
6 .. seealso:: User docs for scripting
7
8 Overview
9 --------
10
11 FRR has the ability to call Lua scripts to perform calculations, make
12 decisions, or otherwise extend builtin behavior with arbitrary user code. This
13 is implemented using the standard Lua C bindings. The supported version of Lua
14 is 5.3.
15
16 C objects may be passed into Lua and Lua objects may be retrieved by C code via
17 a marshalling system. In this way, arbitrary data from FRR may be passed to
18 scripts. It is possible to pass C functions as well.
19
20 The Lua environment is isolated from the C environment; user scripts cannot
21 access FRR's address space unless explicitly allowed by FRR.
22
23 For general information on how Lua is used to extend C, refer to Part IV of
24 "Programming in Lua".
25
26 https://www.lua.org/pil/contents.html#24
27
28
29 Design
30 ------
31
32 Why Lua
33 ^^^^^^^
34
35 Lua is designed to be embedded in C applications. It is very small; the
36 standard library is 220K. It is relatively fast. It has a simple, minimal
37 syntax that is relatively easy to learn and can be understood by someone with
38 little to no programming experience. Moreover it is widely used to add
39 scripting capabilities to applications. In short it is designed for this task.
40
41 Reasons against supporting multiple scripting languages:
42
43 - Each language would require different FFI methods, and specifically
44 different object encoders; a lot of code
45 - Languages have different capabilities that would have to be brought to
46 parity with each other; a lot of work
47 - Languages have vastly different performance characteristics; this would
48 create alot of basically unfixable issues, and result in a single de facto
49 standard scripting language (the fastest)
50 - Each language would need a dedicated maintainer for the above reasons;
51 this is pragmatically difficult
52 - Supporting multiple languages fractures the community and limits the audience
53 with which a given script can be shared
54
55 General
56 ^^^^^^^
57
58 FRR's concept of a script is somewhat abstracted away from the fact that it is
59 Lua underneath. A script in has two things:
60
61 - name
62 - state
63
64 In code:
65
66 .. code-block:: c
67
68 struct frrscript {
69 /* Script name */
70 char *name;
71
72 /* Lua state */
73 struct lua_State *L;
74 };
75
76
77 ``name`` is simply a string. Everything else is in ``state``, which is itself a
78 Lua library object (``lua_State``). This is an opaque struct that is
79 manipulated using ``lua_*`` functions. The basic ones are imported from
80 ``lua.h`` and the rest are implemented within FRR to fill our use cases. The
81 thing to remember is that all operations beyond the initial loading the script
82 take place on this opaque state object.
83
84 There are four basic actions that can be done on a script:
85
86 - load
87 - execute
88 - query state
89 - unload
90
91 They are typically done in this order.
92
93
94 Loading
95 ^^^^^^^
96
97 A snippet of Lua code is referred to as a "chunk". These are simply text. FRR
98 presently assumes chunks are located in individual files specific to one task.
99 These files are stored in the scripts directory and must end in ``.lua``.
100
101 A script object is created by loading a script. This is done with
102 ``frrscript_load()``. This function takes the name of the script and an
103 optional callback function. The string ".lua" is appended to the script name,
104 and the resultant filename is looked for in the scripts directory.
105
106 For example, to load ``/etc/frr/scripts/bingus.lua``:
107
108 .. code-block:: c
109
110 struct frrscript *fs = frrscript_load("bingus", NULL);
111
112 During loading the script is validated for syntax and its initial environment
113 is setup. By default this does not include the Lua standard library; there are
114 security issues to consider, though for practical purposes untrusted users
115 should not be able to write the scripts directory anyway. If desired the Lua
116 standard library may be added to the script environment using
117 ``luaL_openlibs(fs->L)`` after loading the script. Further information on
118 setting up the script environment is in the Lua manual.
119
120
121 Executing
122 ^^^^^^^^^
123
124 After loading, scripts may be executed. A script may take input in the form of
125 variable bindings set in its environment prior to being run, and may provide
126 results by setting the value of variables. Arbitrary C values may be
127 transferred into the script environment, including functions.
128
129 A typical execution call looks something like this:
130
131 .. code-block:: c
132
133 struct frrscript *fs = frrscript_load(...);
134
135 int status_ok = 0, status_fail = 1;
136 struct prefix p = ...;
137
138 struct frrscript_env env[] = {
139 {"integer", "STATUS_FAIL", &status_fail},
140 {"integer", "STATUS_OK", &status_ok},
141 {"prefix", "myprefix", &p},
142 {}};
143
144 int result = frrscript_call(fs, env);
145
146
147 To execute a loaded script, we need to define the inputs. These inputs are
148 passed by binding values to variable names that will be accessible within the
149 Lua environment. Basically, all communication with the script takes place via
150 global variables within the script, and to provide inputs we predefine globals
151 before the script runs. This is done by passing ``frrscript_call()`` an array
152 of ``struct frrscript_env``. Each struct has three fields. The first identifies
153 the type of the value being passed; more on this later. The second defines the
154 name of the global variable within the script environment to bind the third
155 argument (the value) to.
156
157 The script is then executed and returns a general status code. In the success
158 case this will be 0, otherwise it will be nonzero. The script itself does not
159 determine this code, it is provided by the Lua interpreter.
160
161
162 Querying State
163 ^^^^^^^^^^^^^^
164
165 When a chunk is executed, its state at exit is preserved and can be inspected.
166
167 After running a script, results may be retrieved by querying the script's
168 state. Again this is done by retrieving the values of global variables, which
169 are known to the script author to be "output" variables.
170
171 A result is retrieved like so:
172
173 .. code-block:: c
174
175 struct frrscript_env myresult = {"string", "myresult"};
176
177 char *myresult = frrscript_get_result(fs, &myresult);
178
179 ... do something ...
180
181 XFREE(MTYPE_TMP, myresult);
182
183
184 As with arguments, results are retrieved by providing a ``struct
185 frrscript_env`` specifying a type and a global name. No value is necessary, nor
186 is it modified by ``frrscript_get_result()``. That function simply extracts the
187 requested value from the script state and returns it.
188
189 In most cases the returned value will be allocated with ``MTYPE_TMP`` and will
190 need to be freed after use.
191
192
193 Unloading
194 ^^^^^^^^^
195
196 To destroy a script and its associated state:
197
198 .. code-block:: c
199
200 frrscript_unload(fs);
201
202 Values returned by ``frrscript_get_result`` are still valid after the script
203 they were retrieved from is unloaded.
204
205 Note that you must unload and then load the script if you want to reset its
206 state, for example to run it again with different inputs. Otherwise the state
207 from the previous run carries over into subsequent runs.
208
209
210 .. _marshalling:
211
212 Marshalling
213 ^^^^^^^^^^^
214
215 Earlier sections glossed over the meaning of the type name field in ``struct
216 frrscript_env`` and how data is passed between C and Lua. Lua, as a dynamically
217 typed, garbage collected language, cannot directly use C values without some
218 kind of marshalling / unmarshalling system to translate types between the two
219 runtimes.
220
221 Lua communicates with C code using a stack. C code wishing to provide data to
222 Lua scripts must provide a function that marshalls the C data into a Lua
223 representation and pushes it on the stack. C code wishing to retrieve data from
224 Lua must provide a corresponding unmarshalling function that retrieves a Lua
225 value from the stack and converts it to the corresponding C type. These two
226 functions, together with a chosen name of the type they operate on, are
227 referred to as ``codecs`` in FRR.
228
229 A codec is defined as:
230
231 .. code-block:: c
232
233 typedef void (*encoder_func)(lua_State *, const void *);
234 typedef void *(*decoder_func)(lua_State *, int);
235
236 struct frrscript_codec {
237 const char *typename;
238 encoder_func encoder;
239 decoder_func decoder;
240 };
241
242 A typename string and two function pointers.
243
244 ``typename`` can be anything you want. For example, for the combined types of
245 ``struct prefix`` and its equivalent in Lua I have chosen the name ``prefix``.
246 There is no restriction on naming here, it is just a human name used as a key
247 and specified when passing and retrieving values.
248
249 ``encoder`` is a function that takes a ``lua_State *`` and a C type and pushes
250 onto the Lua stack a value representing the C type. For C structs, the usual
251 case, this will typically be a Lua table (tables are the only datastructure Lua
252 has). For example, here is the encoder function for ``struct prefix``:
253
254
255 .. code-block:: c
256
257 void lua_pushprefix(lua_State *L, const struct prefix *prefix)
258 {
259 char buffer[PREFIX_STRLEN];
260
261 zlog_debug("frrlua: pushing prefix table");
262
263 lua_newtable(L);
264 lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
265 lua_setfield(L, -2, "network");
266 lua_pushinteger(L, prefix->prefixlen);
267 lua_setfield(L, -2, "length");
268 lua_pushinteger(L, prefix->family);
269 lua_setfield(L, -2, "family");
270 }
271
272 This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is:
273
274 .. code-block:: c
275
276 { ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
277
278
279 ``decoder`` does the reverse; it takes a ``lua_State *`` and an index into the
280 stack, and unmarshalls a Lua value there into the corresponding C type. Again
281 for ``struct prefix``:
282
283
284 .. code-block:: c
285
286 void *lua_toprefix(lua_State *L, int idx)
287 {
288 struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
289
290 lua_getfield(L, idx, "network");
291 str2prefix(lua_tostring(L, -1), p);
292 lua_pop(L, 1);
293
294 return p;
295 }
296
297 By convention these functions should be called ``lua_to*``, as this is the
298 naming convention used by the Lua C library for the basic types e.g.
299 ``lua_tointeger`` and ``lua_tostring``.
300
301 The returned data must always be copied off the stack and the copy must be
302 allocated with ``MTYPE_TMP``. This way it is possible to unload the script
303 (destroy the state) without invalidating any references to values stored in it.
304
305 To register a new type with its corresponding encoding functions:
306
307 .. code-block:: c
308
309 struct frrscript_codec frrscript_codecs_lib[] = {
310 {.typename = "prefix",
311 .encoder = (encoder_func)lua_pushprefix,
312 .decoder = lua_toprefix},
313 {.typename = "sockunion",
314 .encoder = (encoder_func)lua_pushsockunion,
315 .decoder = lua_tosockunion},
316 ...
317 {}};
318
319 frrscript_register_type_codecs(frrscript_codecs_lib);
320
321 From this point on the type names are available to be used when calling any
322 script and getting its results.
323
324 .. note::
325
326 Marshalled types are not restricted to simple values like integers, strings
327 and tables. It is possible to marshall a type such that the resultant object
328 in Lua is an actual object-oriented object, complete with methods that call
329 back into defined C functions. See the Lua manual for how to do this; for a
330 code example, look at how zlog is exported into the script environment.
331
332
333 Script Environment
334 ------------------
335
336 Logging
337 ^^^^^^^
338
339 For convenience, script environments are populated by default with a ``log``
340 object which contains methods corresponding to each of the ``zlog`` levels:
341
342 .. code-block:: lua
343
344 log.info("info")
345 log.warn("warn")
346 log.error("error")
347 log.notice("notice")
348 log.debug("debug")
349
350 The log messages will show up in the daemon's log output.
351
352
353 Examples
354 --------
355
356 For a complete code example involving passing custom types, retrieving results,
357 and doing complex calculations in Lua, look at the implementation of the
358 ``match script SCRIPT`` command for BGP routemaps. This example calls into a
359 script with a route prefix and attributes received from a peer and expects the
360 script to return a match / no match / match and update result.
361
362 An example script to use with this follows. This script matches, does not match
363 or updates a route depending on how many BGP UPDATE messages the peer has
364 received when the script is called, simply as a demonstration of what can be
365 accomplished with scripting.
366
367 .. code-block:: lua
368
369
370 -- Example route map matching
371 -- author: qlyoung
372 --
373 -- The following variables are available to us:
374 -- log
375 -- logging library, with the usual functions
376 -- prefix
377 -- the route under consideration
378 -- attributes
379 -- the route's attributes
380 -- peer
381 -- the peer which received this route
382 -- RM_FAILURE
383 -- status code in case of failure
384 -- RM_NOMATCH
385 -- status code for no match
386 -- RM_MATCH
387 -- status code for match
388 -- RM_MATCH_AND_CHANGE
389 -- status code for match-and-set
390 --
391 -- We need to set the following out values:
392 -- action
393 -- Set to the appropriate status code to indicate what we did
394 -- attributes
395 -- Setting fields on here will propagate them back up to the caller if
396 -- 'action' is set to RM_MATCH_AND_CHANGE.
397
398
399 log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string)
400
401 function on_match (prefix, attrs)
402 log.info("Match")
403 action = RM_MATCH
404 end
405
406 function on_nomatch (prefix, attrs)
407 log.info("No match")
408 action = RM_NOMATCH
409 end
410
411 function on_match_and_change (prefix, attrs)
412 action = RM_MATCH_AND_CHANGE
413 log.info("Match and change")
414 attrs["metric"] = attrs["metric"] + 7
415 end
416
417 special_routes = {
418 ["172.16.10.4/24"] = on_match,
419 ["172.16.13.1/8"] = on_nomatch,
420 ["192.168.0.24/8"] = on_match_and_change,
421 }
422
423
424 if special_routes[prefix.network] then
425 special_routes[prefix.network](prefix, attributes)
426 elseif peer.stats.update_in % 3 == 0 then
427 on_match(prefix, attributes)
428 elseif peer.stats.update_in % 2 == 0 then
429 on_nomatch(prefix, attributes)
430 else
431 on_match_and_change(prefix, attributes)
432 end
433