├── .gitignore ├── LICENSE ├── README.md ├── TODO ├── client ├── meson.build ├── snapshot.c └── vplsh.c ├── daemon ├── bstr.c ├── bstr.h ├── compat.h ├── configcmd.c ├── configcmd.h ├── configdb.c ├── configdb.h ├── configstore.c ├── configstore.h ├── controller.h ├── devname.c ├── fsnotify.c ├── hwbinding.c ├── ip_addr.h ├── main.c ├── meson.build ├── mnlutil.c ├── mrtstat.c ├── mrtstat.h ├── nlmsg.c ├── nlmsg.h ├── parser.c ├── parser.h ├── port.c ├── protobuf.c ├── protobuf.h ├── request.c ├── snapshot.c ├── team.c ├── team.h ├── topic.c ├── tunnel.c ├── util.c ├── vplane.c └── vplane.h ├── debian ├── changelog ├── compat ├── control ├── copyright ├── golang-github-danos-vyatta-controller-protobuf-dev.install ├── libvplaned-dev.install ├── libvplaned1.install ├── libvyatta-controller-proto-dev.install ├── libvyatta-controller-proto-support.install ├── libvyatta-controller-proto1.install ├── rules ├── source │ └── format ├── vplane-controller.init.d ├── vplane-controller.install ├── vplane-controller.lintian-overrides ├── vplane-controller.preinst ├── vplane-controller.service └── vplane-controller.socket ├── examples ├── controller.conf └── dataplane.conf ├── lib ├── meson.build ├── vplaned.h ├── vplaned_cfg.c ├── vplaned_cfg.h ├── vplaned_cstore.c ├── vplaned_cstore.h ├── vplaned_event.c └── vplaned_event.h ├── meson.build ├── meson_options.txt ├── protobuf ├── VPlanedEnvelope.proto └── meson.build ├── scripts └── vyatta-generate-pb-perl.pl ├── snmp ├── .gitignore ├── meson.build └── vplane-snmp.c ├── test ├── example │ ├── bfdtest.c │ └── meson.build ├── module │ ├── meson.build │ └── src │ │ ├── all_tests.cpp │ │ ├── hwbinding_cpputest.cpp │ │ ├── libvplaned_cfg_cpputest.cpp │ │ ├── libvplaned_cstore_cpputest.cpp │ │ ├── mocks │ │ ├── common_mocks.cpp │ │ ├── cpputest_comparators.h │ │ ├── fabric_crypto_mocks.cpp │ │ ├── fabric_topo_mocks.cpp │ │ ├── parser_mocks.cpp │ │ ├── snapshot_mocks.cpp │ │ └── stubs.h │ │ ├── nlmsg_cpputest.cpp │ │ ├── parser_cpputest.cpp │ │ ├── request_cpputest.cpp │ │ ├── snapshot_cpputest.cpp │ │ ├── testcfgs │ │ ├── 2vplanes.conf │ │ ├── 2vplanes6.conf │ │ ├── bad.conf │ │ ├── default.conf │ │ ├── defvplanes.conf │ │ ├── interface.conf │ │ ├── interface.conf2 │ │ ├── local.conf │ │ ├── novplanes.conf │ │ ├── routesourcebad.conf │ │ ├── routesourcekernel.conf │ │ └── routesourcerib.conf │ │ └── vplane_cpputest.cpp ├── resync.c └── tunnel.c └── tools └── hwbinding /.gitignore: -------------------------------------------------------------------------------- 1 | # Debian rules 2 | build-stamp 3 | debian/tmp 4 | debian/files 5 | debian/libvplaned-dev/ 6 | debian/vplane-controller/ 7 | debian/vplane-controller-dbg/ 8 | debian/golang-github-danos-vyatta-controller-protobuf-dev/ 9 | debian/libvplaned1/ 10 | debian/libvyatta-controller-proto-dev/ 11 | debian/libvyatta-controller-proto-support/ 12 | debian/libvyatta-controller-proto1/ 13 | debian/*.log 14 | debian/*.substvars 15 | debian/*.debhelper 16 | debian/debhelper-build-stamp 17 | # executable 18 | daemon/vplaned 19 | client/snapshot 20 | client/vplsh 21 | # Test 22 | test/module/build 23 | # Normal rules 24 | .* 25 | *.o 26 | *.o.* 27 | *.a 28 | *.s 29 | *.ko 30 | *.so 31 | *.so.dbg 32 | *.mod.c 33 | *.i 34 | *.lst 35 | *.symtypes 36 | *.order 37 | modules.builtin 38 | *.elf 39 | *.bin 40 | *.gz 41 | *.bz2 42 | *.lzma 43 | *.xz 44 | *.lzo 45 | *.patch 46 | *.gcno 47 | # 48 | # Top-level generic files 49 | # 50 | /tags 51 | /TAGS 52 | 53 | # git files that we don't want to ignore even it they are dot-files 54 | !.gitignore 55 | 56 | # cscope files 57 | cscope.* 58 | 59 | *.orig 60 | *.rej 61 | *~ 62 | \#*# 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DANOS dataplane controller 2 | 3 | The controller acts as a conduit between "applications" (configuration, 4 | routing) and the dataplane (__vplane__). The controller daemon 5 | (__vplaned__) consists of a number of connections: 6 | 7 | * NETLINK connections into the kernel in order to receive (routing, 8 | IPsec, L2TP & TEAM) updates. Note that only non-unicast routes are 9 | retrived from the kernel, unicast routes are published __directly__ to 10 | the dataplane by RIBd (the route broker). 11 | 12 | * A Command Proxy (ZMQ REQUEST-REPLY) connection used to receive 13 | additional configuration information (see Configuration Database) 14 | 15 | * A Request (ZMQ ROUTER-DEALER) connection to one or more dataplane 16 | processes. The connection is used by a dataplane to register itself and 17 | its associated local interfaces. 18 | 19 | * A Publication (ZMQ PUB-SUB) connection to one or more dataplane 20 | processes. The connection is used to publish ("broadcast") configuration 21 | updates to dataplanes. 22 | 23 | * A Query (ZMQ REQUEST-REPLY) connection. The connection allows 24 | applications to request operational state (e.g. list of dataplane 25 | instances) and configuration information (e.g. current IP address) from 26 | the controller. 27 | 28 | The control-plane between __vplaned__ and __vplane__ operates 29 | over a series of local IPC channels (the Request and Publication 30 | connections). 31 | 32 | ## Licensing 33 | 34 | The controller is licensed under GNU LGPL-2.1. 35 | 36 | ## Snapshot Database 37 | 38 | The controller maintains a snapshot database that consists of the 39 | netlink messages received from the kernel, arranged as a series of lists 40 | and hash tables. 41 | 42 | Each received netlink message is parsed in order to produce a "topic" (a 43 | string representation of the message contents). The combined object 44 | (topic string + netlink data) is inserted into the snapshot database and 45 | published directly to any/all connected dataplane(s). 46 | 47 | The object topic is used as the key in an associated hash table. 48 | 49 | Following initial contact with the controller a dataplane asks for the 50 | current system configuration - a "__WHATSUP?__" request message. The 51 | controller responds by "dumping" the contents of the database down to 52 | the dataplane. The leading word of the topic string is used by the 53 | dataplane to dispatch the object to the appropriate subsystem. 54 | 55 | ## Configuration Database 56 | 57 | Whilst the majority of the network configuration can be represented 58 | through netlink messages, there are elements of application state that 59 | require additional configuration. 60 | 61 | The configuration store (__cstore__) consists of a key-value 62 | database. Applications (YANG backend scripts) use a Perl or Python API 63 | to inject a JSON encoded message into the controller. The message 64 | consists of two parts, a key (or path) and a value. The value part is an 65 | arbitrary string that is passed down (published) to the dataplane for 66 | processing by the control module. 67 | 68 | Other than certain parts of the path element, the controller has no 69 | understanding of the message; the message is simply stored and the value 70 | part forwarded to the dataplane. Similar to netlink messages, the 71 | dataplane uses the leading word of the value part to dispatch the 72 | message to the appropriate subsystem. 73 | 74 | For example, this is the "raw" JSON as processed by the Controller: 75 | 76 | ``` 77 | {"policy":{"route":{"pbr":{"cust1":{"rule":{"10":{"__SET__": 78 | "npf-cfg add pbr:cust1 10 action=accept family=inet dst-addr=192.168.102.0/24 rproc=pathmon(cust1,cust1) tag=1 ", 79 | "__INTERFACE__":"ALL"} 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | And this is the command ("topic" and "value") that is published to the 89 | dataplane: 90 | 91 | ``` 92 | Publish [119] 'npf-cfg add ', 'npf-cfg add pbr:cust1 10 action=accept family=inet dst-addr=192.168.102.0/24 rproc=pathmon(cust1,cust1) tag=1 ' 93 | ``` 94 | 95 | The Perl and Python library modules can be found in the 96 | vyatta-cfg-dataplane repository. 97 | 98 | As with the Snapshot Database, in response to a "__WHATSUP?__" request, 99 | the controller pushes down the contents of the __cstore__ database to 100 | the dataplane. 101 | 102 | ## Snapshot utility 103 | 104 | The snapshot utility ('/opt/vyatta/bin/snapshot') can be used to dump 105 | the contents of the snapshot database maintained by the controller. 106 | 107 | ``` 108 | vyatta@vyatta:~$ /opt/vyatta/bin/snapshot --help 109 | Usage: snapshot [OPTION...] 110 | 111 | vPlaned snapshot. 112 | 113 | -d, --debug Include topic information 114 | -c, --cstore Dump the cstore database 115 | -i, --ifindex Dump specified ifindex only 116 | -h, --help Display help and exit 117 | 118 | vyatta@vyatta:~$ 119 | ``` 120 | 121 | By default the utility will collect the netlink details and feed them 122 | into ip monitor: 123 | 124 | ``` 125 | vyatta@vyatta:~$ /opt/vyatta/bin/snapshot 126 | [LINK]12: lo1: mtu 1500 qdisc noqueue state UNKNOWN group default 127 | link/ether d2:ae:37:94:3e:8e brd ff:ff:ff:ff:ff:ff 128 | [LINK]12: lo1: mtu 1500 state UNKNOWN 129 | link/ether d2:ae:37:94:3e:8e 130 | [LINK]7: .spathintf: mtu 1500 qdisc fq_codel state UNKNOWN group default 131 | link/none 132 | [LINK]1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default 133 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 134 | : 135 | : 136 | [NETCONF]mpls dev lo input off 137 | [NETCONF]ipv4 dev dp0p1s1.103 forwarding on rp_filter off mc_forwarding off proxy_neigh off ignore_routes_with_linkdown off 138 | [NETCONF]ipv4 dev lo1 forwarding on rp_filter off mc_forwarding off proxy_neigh off ignore_routes_with_linkdown off 139 | vyatta@vyatta:~$ 140 | ``` 141 | 142 | The '--cstore' option will display the contents of the cstore database: 143 | 144 | ``` 145 | vyatta@vyatta:~$ /opt/vyatta/bin/snapshot -c 146 | [32] mpls labeltablesize 0 147 | 148 | [33] mpls defaultttl 255 149 | 150 | [34] mpls ipttlpropagate enable 151 | [109] ecmp mode hrw 152 | [129] npf-cfg add dscp-group:default-group-high-drop 0 8;10;16;18 153 | [132] npf-cfg add dscp-group:default-group-low-drop 0 0;1;2;3;4;5;6;7;9;11;12;13;14;15;17;19;20;21;22;23;41;42;43;44;45;49;50; 154 | : 155 | : 156 | [2346] qos 11 enable 157 | [2680] THATSALLFOLKS! 158 | vyatta@vyatta:~$ 159 | ``` 160 | 161 | ## Dataplane Database 162 | 163 | The controller (__vplaned__) derives a core set of attributes from its 164 | configuration file (__/etc/vyatta/controller.conf__). Among the 165 | attributes is the local IP address of the __vplaned__ daemon together 166 | with a list of defined dataplane (__vplane__) instances. For each 167 | instance the file defines the IP address of the __vplane__ together with 168 | its __UUID__. 169 | 170 | There is a single defined __vplane__. The IP address of 171 | both controller and __vplane__ is the loopback address (127.0.0.1) and 172 | the __UUID__ is simply zero (00000000-0000-0000-0000-000000000000). 173 | 174 | The dataplane makes contact with the controller via the Request 175 | connection. 176 | 177 | * The controller establishes its authenticity (UUID match together with 178 | any ZMQ socket authentication) - "__CONNECT__", "__ACCEPT__" and 179 | "__CONFQUERY__" messages. 180 | 181 | * The dataplane reports on each of its hardware interfaces - 182 | "__NEWPORT__" messages. The "__NEWPORT__" message is used to create 183 | the "local" interface representation, e.g. dp0p1s1 or dp2p1s3. 184 | 185 | * The dataplane requests the current system configuration - "__WHATSUP?__" message. 186 | 187 | ## Unit Testing 188 | 189 | The unit tests are executed as part of the default package build and 190 | __must__ be kept passing with every commit. 191 | 192 | You should consider adding unit tests for any new functionality being added. 193 | 194 | The tests are built around the [CppUTest][3] harness and typically 195 | exercise the (external) functions associated with a single module. 196 | 197 | ## Coding Style 198 | 199 | Code conforms to the [linux kernel coding style][1], and [checkpatch][2] 200 | can be used to find common style issues. Probably the easiest way to 201 | check an update is to use the wrapper provided by the dataplane 202 | (`vyatta-dataplane/scripts/checkpatch_wrapper.sh`) 203 | 204 | Please fix any warnings it reports, or be prepared to justify the exception 205 | during code review. 206 | 207 | [1]: https://www.kernel.org/doc/Documentation/CodingStyle "Linux Kernel Coding Style" 208 | [2]: https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl "checkpatch script" 209 | [3]: http://cpputest.github.io/ "Cpputest Unit Test Framework" 210 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Implement Statistics message? 2 | 3 | * Add Man pages 4 | 5 | * Dynamic reconfigure of broker on SIGUSR1 6 | -------------------------------------------------------------------------------- /client/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | executable( 5 | 'vplsh', 6 | sources: ['vplsh.c'], 7 | dependencies: [ 8 | czmq_dep, 9 | json_dep, 10 | libedit_dep, 11 | vplaned_dep 12 | ], 13 | install: true, 14 | install_dir : '/opt/vyatta/bin' 15 | ) 16 | 17 | executable( 18 | 'snapshot', 19 | sources: ['snapshot.c'], 20 | dependencies: [ 21 | czmq_dep, 22 | json_dep, 23 | mnl_dep, 24 | vplaned_dep 25 | ], 26 | install: true, 27 | install_dir : '/opt/vyatta/bin' 28 | ) 29 | -------------------------------------------------------------------------------- /daemon/compat.h: -------------------------------------------------------------------------------- 1 | /* not included in any kernel headers */ 2 | #ifndef AF_MPLS 3 | # define AF_MPLS 28 4 | #endif 5 | 6 | #ifdef RD_DEFAULT 7 | #define VRF_DEFAULT_ID RD_DEFAULT 8 | #else 9 | #define VRF_DEFAULT_ID 1 10 | #endif 11 | -------------------------------------------------------------------------------- /daemon/configcmd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Supports operations on the configuration store system. Updates, retrievals, 3 | * ordering of commands. 4 | * 5 | * Copyright (c) 2018-2021, AT&T Intellectual Property. All rights reserved. 6 | * Copyright (c) 2012-2017 by Brocade Communications Systems, Inc. 7 | * All rights reserved. 8 | * 9 | * SPDX-License-Identifier: LGPL-2.1-only 10 | * 11 | **/ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "bstr.h" 21 | #include "controller.h" 22 | #include "configdb.h" 23 | #include "configcmd.h" 24 | 25 | /* Global *temporary* containers */ 26 | static zlist_t *_root_cmd_coll; /* new cmds to dispatch */ 27 | 28 | /* 29 | Cache is kept until next conf command. In 30 | which case this is flushed. Handles case 31 | where multiple dataplanes restart--this only 32 | costs a single global tree walk in this case. 33 | */ 34 | static zlist_t *_root_resync_coll; /* cache of resync commands */ 35 | 36 | /* Global active interface collection */ 37 | static zhash_t *_intf_coll; 38 | 39 | /* 40 | * Helper function for active interface collection 41 | */ 42 | zhash_t* 43 | get_intf_coll(void) 44 | { 45 | if (!_intf_coll) { 46 | _intf_coll = zhash_new(); 47 | zhash_autofree(_intf_coll); 48 | } 49 | return _intf_coll; 50 | } 51 | 52 | bool 53 | insert_intf_coll(const char *name) 54 | { 55 | if (!name) 56 | return false; 57 | 58 | zhash_t *coll = get_intf_coll(); 59 | 60 | return zhash_insert(coll, (char *)name, (void *)name) == 0; 61 | } 62 | 63 | void 64 | delete_intf_coll(const char *name) 65 | { 66 | if (!name) 67 | return; 68 | 69 | zhash_t *coll = get_intf_coll(); 70 | 71 | zhash_delete(coll, name); 72 | } 73 | 74 | bool 75 | suppress_intf_cmd(const char *iface) 76 | { 77 | if (!iface) 78 | return false; 79 | 80 | zhash_t *coll = get_intf_coll(); 81 | if (strcasecmp(iface, "ALL") && 82 | !zhash_lookup(coll, iface)) 83 | return true; 84 | return false; 85 | } 86 | 87 | /* 88 | Helper functions below for configstore interface 89 | */ 90 | zlist_t *get_cmd_coll(void) 91 | { 92 | return _root_cmd_coll; 93 | } 94 | 95 | void reset_cmd_coll(void) 96 | { 97 | zlist_destroy(&_root_cmd_coll); 98 | _root_cmd_coll = NULL; 99 | } 100 | 101 | int publish_config_cmd(command_node_t *cmd, uint64_t *msg_seq) 102 | { 103 | if (cmd == NULL || cmd->_node == NULL) 104 | return -1; 105 | 106 | if (cmd->_node->_topic != NULL) { 107 | if (cmd->_ephemeral != NULL) { 108 | publish_cmd(cmd->_node->_topic, ++(*msg_seq), 109 | cmd->_ephemeral, cmd->_node->_bin); 110 | free(cmd->_ephemeral); 111 | } else { 112 | publish_cmd(cmd->_node->_topic, ++(*msg_seq), 113 | cmd->_node->_value, cmd->_node->_bin); 114 | } 115 | } 116 | cmd->_node->_seq = *msg_seq; /* save sequence number for resyncing */ 117 | return 0; 118 | } 119 | 120 | /* 121 | Add received command to processing list. 122 | */ 123 | void add_cmd(config_node_t *config_node, const char *ephemeral) 124 | { 125 | command_node_t *cmd = malloc(sizeof(command_node_t)); 126 | 127 | if (cmd == NULL) { 128 | err("failure to allocate memory"); 129 | return; 130 | } 131 | cmd->_node = config_node; 132 | 133 | if (ephemeral == NULL) 134 | cmd->_ephemeral = NULL; 135 | else 136 | cmd->_ephemeral = strdup(ephemeral); 137 | 138 | if (!_root_cmd_coll) 139 | _root_cmd_coll = zlist_new(); 140 | 141 | zlist_append(_root_cmd_coll, (void *)cmd); 142 | } 143 | 144 | /* 145 | Build up resync command list from configuration tree. 146 | */ 147 | static void walk_db(struct lh_table *db, zlist_t **coll, struct bstr *db_key) 148 | { 149 | struct lh_entry *e; 150 | int curr_len = db_key->len; 151 | 152 | if (db == NULL) 153 | return; 154 | 155 | lh_foreach(db, e) { 156 | config_node_t *config_node = (config_node_t *) e->v; 157 | 158 | if (config_node->_value == NULL) { 159 | bstr_addf(db_key, "%s ", (char *)e->k); 160 | } else { 161 | command_node_t *cmd = malloc(sizeof(command_node_t)); 162 | if (cmd == NULL) { 163 | err("failure to allocate memory"); 164 | return; 165 | } 166 | cmd->_node = config_node; 167 | cmd->_ephemeral = NULL; 168 | cmd->_db_key = strdup((char *)db_key->buf); 169 | if (cmd->_db_key == NULL) { 170 | err("failure to allocate memory for db key"); 171 | free(cmd); 172 | return; 173 | } 174 | 175 | /* 176 | * Handles required sorting of sequence 177 | * value. Needed since configuration data 178 | * arrives in expected replay order. 179 | * 180 | * Sorting below is inefficient. 181 | * TODO: Needs to be updated with a single 182 | * post sorting operation. 183 | */ 184 | if (*coll == NULL) 185 | *coll = zlist_new(); 186 | 187 | zlist_append(*coll, (void *)cmd); 188 | } 189 | walk_db(config_node->_hash, coll, db_key); 190 | 191 | /* remove names added by lower nodes from the key */ 192 | bstr_setlen(db_key, curr_len); 193 | } 194 | } 195 | 196 | static int resync_comp(void *item1, void *item2) 197 | { 198 | if (((command_node_t *) item1)->_node->_seq == -1ULL) 199 | return -1; 200 | 201 | return ((command_node_t *) item1)->_node->_seq > 202 | ((command_node_t *) item2)->_node->_seq; 203 | } 204 | 205 | zlist_t *get_resync_coll(void) 206 | { 207 | if (!_root_resync_coll) { 208 | struct bstr db_key; 209 | 210 | bstr_init(&db_key, 128 /* initial length */); 211 | 212 | /* rebuild resync */ 213 | walk_db(get_config_coll(), &_root_resync_coll, &db_key); 214 | if (_root_resync_coll) 215 | zlist_sort(_root_resync_coll, resync_comp); 216 | 217 | bstr_release(&db_key); 218 | } 219 | 220 | return _root_resync_coll; 221 | } 222 | 223 | void flush_resync(void) 224 | { 225 | command_node_t *n; 226 | 227 | if (!_root_resync_coll) 228 | return; 229 | 230 | while ((n = (command_node_t *) zlist_pop(_root_resync_coll))) { 231 | free(n->_db_key); 232 | free(n); /* ephemeral should be null on resync */ 233 | } 234 | 235 | zlist_destroy(&_root_resync_coll); 236 | _root_resync_coll = NULL; 237 | } 238 | -------------------------------------------------------------------------------- /daemon/configcmd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header supporting operations on the configuration store system. 3 | * Updates, retrievals, ordering of commands. 4 | * 5 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 6 | * Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. 7 | * Copyright (c) 2012-2017 by Brocade Communications Systems, Inc. 8 | * All rights reserved. 9 | * 10 | * SPDX-License-Identifier: LGPL-2.1-only 11 | * 12 | **/ 13 | #ifndef __CONFIGCMD_H__ 14 | #define __CONFIGCMD_H__ 15 | 16 | /* Function decls */ 17 | zhash_t *get_intf_coll(void); 18 | bool insert_intf_coll(const char *name); 19 | void delete_intf_coll(const char *name); 20 | bool suppress_intf_cmd(const char *iface); 21 | zlist_t *get_cmd_coll(void); 22 | void reset_cmd_coll(void); 23 | zlist_t *get_resync_coll(void); 24 | int publish_config_cmd(command_node_t *, uint64_t *); 25 | 26 | #endif /* __CONFIGCMD_H__ */ 27 | -------------------------------------------------------------------------------- /daemon/configdb.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Storage of unique hashed commands. Provides mapping between 3 | * our configuration tree and commands to the dataplane. Sits 4 | * behind the configstore interface. 5 | * 6 | * Copyright (c) 2018-2019, 2021 AT&T Intellectual Property. All rights reserved. 7 | * Copyright (c) 2012-2015 by Brocade Communications Systems, Inc. 8 | * All rights reserved. 9 | * 10 | * SPDX-License-Identifier: LGPL-2.1-only 11 | * 12 | **/ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "controller.h" 22 | #include "configdb.h" 23 | 24 | #define HASH_SIZE 256 25 | 26 | /* 27 | Externally available resync commands. Note that _root_config_coll 28 | is cleared after a new configuration command has been received. 29 | _root_config_coll is regenerated on resync request and _root_config_coll 30 | is null. 31 | */ 32 | /* Global config container (tree storage) */ 33 | static lh_table *_root_config_coll; 34 | 35 | /* Interface hint keyword */ 36 | #define INTERFACE_NODE "__INTERFACE__" 37 | 38 | /* Actions sent via configuration tree, parent of command */ 39 | #define DELETE_ACTION "__DELETE__" 40 | #define SET_ACTION "__SET__" 41 | #define ACTIVE_ACTION "__ACTIVE__" 42 | 43 | #define PROTOBUF_TOKEN "__PROTOBUF__" 44 | 45 | /* 46 | Callback function handling release of configuration node structure. 47 | Note, this code indirectly calls child hash release functions. 48 | */ 49 | static void 50 | config_coll_free(struct lh_entry *e) 51 | { 52 | config_node_t *cv = (config_node_t *)e->v; 53 | 54 | /* Initiates recursive call to children */ 55 | lh_table *db = cv->_hash; 56 | if (db != NULL) 57 | lh_table_free(db); 58 | 59 | free(cv->_value); 60 | free(cv->_topic); 61 | free(cv->_interface); 62 | 63 | /* key is strdup'ed manually because json-c does not make a copy */ 64 | free((void *)e->k); 65 | 66 | free(cv); 67 | } 68 | 69 | /* 70 | * API change (breakage). The json-c hash table name argument got 71 | * dropped around version 0.13.x (see json-c:1ae4b50b) 72 | */ 73 | static inline lh_table * 74 | my_lh_kchar_table_new(int size, lh_entry_free_fn free_fn) 75 | { 76 | #if JSON_C_MINOR_VERSION > 13 77 | return lh_kchar_table_new(size, free_fn); 78 | #else 79 | return lh_kchar_table_new(size, "config_hash", free_fn); 80 | #endif 81 | } 82 | 83 | /* 84 | Create single config_node_t structure 85 | */ 86 | 87 | static config_node_t * 88 | create_config_node(char *key __unused) 89 | { 90 | config_node_t *cv = malloc(sizeof(config_node_t)); 91 | if (cv == NULL) { 92 | err("failure to allocate memory"); 93 | return cv; 94 | } 95 | cv->_hash = my_lh_kchar_table_new(HASH_SIZE, config_coll_free); 96 | cv->_seq = -1; 97 | cv->_topic = NULL; 98 | cv->_value = NULL; 99 | cv->_interface = NULL; 100 | cv->_bin = false; 101 | return cv; 102 | } 103 | 104 | /* 105 | Helper function to extract topic from command (likely to go away) 106 | */ 107 | static char* 108 | extract_topic(const char *line) 109 | { 110 | char topic[BUFSIZ], *tmp = NULL; 111 | topic[0] = '\0'; 112 | /* For now... grab first two entries as topic */ 113 | /* NEED to fix this up so that topic is pulled from config cmd */ 114 | char *token = strtok_r((char *)line, " ", &tmp); 115 | int ct = 0; 116 | while (token != NULL && ct < 2) { 117 | strcat(topic, token); 118 | strcat(topic, " "); 119 | token = strtok_r(NULL, " ", &tmp); 120 | ++ct; 121 | } 122 | if (ct == 2) 123 | return strdup(topic); 124 | 125 | return NULL; 126 | } 127 | 128 | /* 129 | Recursive call to merge json_object into hashed configuration tree. Handles 130 | Sets/Deletes as identifed by the JSON values. 131 | */ 132 | static void 133 | merge_json_to_db(lh_table *db, json_object *jobj, config_node_t *p_cv) 134 | { 135 | if (db == NULL || jobj == NULL) 136 | return; 137 | 138 | enum json_type type = json_object_get_type(jobj); 139 | if (type == json_type_object) { 140 | json_object_iter iter; 141 | enum json_type itertype; 142 | 143 | json_object_object_foreachC(jobj, iter) { 144 | itertype = json_object_get_type(iter.val); 145 | if (itertype == json_type_string) { 146 | if (strcmp(iter.key, DELETE_ACTION) == 0) { /* DELETE */ 147 | const char *cmd = json_object_get_string(iter.val); 148 | add_cmd(p_cv, cmd); 149 | 150 | /* handle binary data key */ 151 | json_object *bin_jobj; 152 | ; 153 | if (json_object_object_get_ex(jobj, 154 | PROTOBUF_TOKEN, 155 | &bin_jobj) && 156 | bin_jobj && 157 | json_object_is_type(bin_jobj, json_type_boolean)) 158 | p_cv->_bin = json_object_get_boolean(bin_jobj); 159 | 160 | lh_table_free(p_cv->_hash); /* free nodes below this point */ 161 | p_cv->_hash = NULL; 162 | free(p_cv->_topic); 163 | p_cv->_topic = extract_topic(cmd); 164 | } else if (strcmp(iter.key, SET_ACTION) == 0 || 165 | strcmp(iter.key, ACTIVE_ACTION) == 0) { /* SET or ACTIVE */ 166 | /* two possible actions, update or create */ 167 | config_node_t *cv = NULL; 168 | struct lh_entry *e = lh_table_lookup_entry(db, (void *)SET_ACTION); 169 | if (e == NULL) { /* create new entry */ 170 | cv = create_config_node(SET_ACTION); 171 | if (cv == NULL) 172 | return; 173 | 174 | const char *cmd = json_object_get_string(iter.val); 175 | cv->_value = strdup(cmd); 176 | cv->_topic = extract_topic(cmd); 177 | 178 | add_cmd(cv, NULL); 179 | 180 | /* handle binary data key */ 181 | json_object *bin_jobj; 182 | if (json_object_object_get_ex(jobj, 183 | PROTOBUF_TOKEN, 184 | &bin_jobj) && 185 | bin_jobj && 186 | json_object_is_type(bin_jobj, json_type_boolean)) 187 | cv->_bin = json_object_get_boolean(bin_jobj); 188 | 189 | /* Handle interface hint */ 190 | json_object *iface_jobj = NULL; 191 | json_object_object_get_ex(jobj, INTERFACE_NODE, &iface_jobj); 192 | if (iface_jobj != NULL && json_object_is_type(iface_jobj, json_type_string)) { 193 | const char *iface = json_object_get_string(iface_jobj); 194 | cv->_interface = strdup(iface); /* copy interface hint */ 195 | } 196 | 197 | lh_table_insert(db, (void *)strdup(SET_ACTION), (void *)cv); 198 | } else { /* update existing entry */ 199 | cv = (config_node_t *)e->v; 200 | free(cv->_value); 201 | 202 | const char *cmd = json_object_get_string(iter.val); 203 | cv->_value = strdup(cmd); 204 | /* Need to look up iv entry so this can be republished again */ 205 | add_cmd(cv, NULL); 206 | 207 | } 208 | } 209 | continue; /* string object is end of recursion */ 210 | } 211 | 212 | /* 213 | * Skip anything other than an object, 214 | * i.e. ignore the PROTOBUF "tag". 215 | */ 216 | if (itertype != json_type_object) 217 | continue; 218 | 219 | /* RECURSION, i.e. Object node */ 220 | struct lh_entry *e = lh_table_lookup_entry(db, (void *)iter.key); 221 | if (e == NULL) { /* create */ 222 | p_cv = create_config_node(iter.key); 223 | if (p_cv == NULL) 224 | return; 225 | 226 | lh_table_insert(db, (void *)strdup(iter.key), 227 | (void *)p_cv); 228 | } else { /* update */ 229 | p_cv = ((config_node_t *)e->v); 230 | if (p_cv->_hash == NULL) 231 | p_cv->_hash = 232 | my_lh_kchar_table_new(HASH_SIZE, 233 | config_coll_free); 234 | 235 | } 236 | merge_json_to_db(p_cv->_hash, iter.val, p_cv); 237 | } 238 | } 239 | } 240 | 241 | 242 | /* 243 | Entry function to update internal configuration db. 244 | */ 245 | void 246 | update_db(json_object *jobj) 247 | { 248 | if (!_root_config_coll) 249 | _root_config_coll 250 | = my_lh_kchar_table_new(HASH_SIZE, config_coll_free); 251 | 252 | flush_resync(); /* empty resync cache */ 253 | merge_json_to_db(_root_config_coll, jobj, NULL); 254 | } 255 | 256 | 257 | lh_table* 258 | get_config_coll(void) 259 | { 260 | return _root_config_coll; 261 | } 262 | 263 | void 264 | config_coll_destroy(void) 265 | { 266 | if (_root_config_coll) { 267 | lh_table_free(_root_config_coll); 268 | _root_config_coll = NULL; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /daemon/configdb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2013-2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | 9 | #ifndef __CONFIGDB_H__ 10 | #define __CONFIGDB_H__ 11 | 12 | typedef struct { 13 | lh_table *_hash; 14 | char *_value; 15 | char *_topic; 16 | char *_interface; 17 | bool _bin; 18 | uint64_t _seq; 19 | } config_node_t; 20 | 21 | 22 | /* TODO roll LL and command_node_t into one w/ quicksort */ 23 | typedef struct { 24 | /* ACTION=DELETE, removed after publishing */ 25 | char *_ephemeral; 26 | config_node_t *_node; 27 | char *_db_key; 28 | } command_node_t; 29 | 30 | /* Function decls */ 31 | void update_db(json_object *); 32 | void add_cmd(config_node_t *, const char *); 33 | void flush_resync(void); 34 | lh_table *get_config_coll(void); 35 | void config_coll_destroy(void); 36 | 37 | #endif /* __CONFIGDB_H__ */ 38 | -------------------------------------------------------------------------------- /daemon/configstore.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Mostly interface between the generic configuration store 3 | * and the rest of the controller. The external requirements 4 | * are to interpret single commands and to provide a snapshot 5 | * of all commands during a resync request. 6 | * 7 | * Copyright (c) 2018-2021 AT&T Intellectual Property. All rights reserved. 8 | * Copyright (c) 2012-2017 by Brocade Communications Systems, Inc. 9 | * All rights reserved. 10 | * 11 | * SPDX-License-Identifier: LGPL-2.1-only 12 | * 13 | **/ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "protobuf.h" 28 | #include "controller.h" 29 | #include "configdb.h" 30 | #include "configstore.h" 31 | #include "configcmd.h" 32 | 33 | static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; 34 | 35 | typedef struct { 36 | zsock_t *socket; /* socket to send to */ 37 | zframe_t *client; /* identity of client requesting */ 38 | } target_t; 39 | 40 | /* 41 | Configuration command entry point. Expects JSON formatted request 42 | with specific value nodes (see documentation else in this file and 43 | in configdb.c). 44 | 45 | Currently locked via mutex (can be upgraded as needed) 46 | */ 47 | int config_cmd(const char *line) 48 | { 49 | //parse json 50 | enum json_tokener_error error = json_tokener_success; 51 | json_object *jobj = json_tokener_parse_verbose(line, &error); 52 | if (jobj == NULL) { 53 | err("json_tokener_parse error: %s, \"%s\"", 54 | json_tokener_error_desc(error), line); 55 | return -1; 56 | } 57 | 58 | pthread_mutex_lock(&_mutex); 59 | update_db(jobj); 60 | json_object_put(jobj); /* release ref count */ 61 | pthread_mutex_unlock(&_mutex); 62 | return 0; 63 | } 64 | 65 | /* 66 | Dispatch new commands as as received by config_cmd. Also updates 67 | internal data representation. 68 | */ 69 | void send_cmds(uint64_t *msg_seq) 70 | { 71 | pthread_mutex_lock(&_mutex); 72 | zlist_t *coll = get_cmd_coll(); 73 | if (!coll) { 74 | pthread_mutex_unlock(&_mutex); 75 | return; 76 | } 77 | 78 | command_node_t *cmd; 79 | while ( (cmd = zlist_pop(coll)) ) { 80 | /* suppress cmd if intf doesn't exist 81 | * and isn't a deletion command 82 | */ 83 | if (cmd->_ephemeral == NULL && 84 | suppress_intf_cmd(cmd->_node->_interface)) { 85 | free(cmd); 86 | continue; 87 | } 88 | 89 | if (publish_config_cmd(cmd, msg_seq) != 0) 90 | err("error sending commands to dataplane"); 91 | 92 | free(cmd); 93 | } 94 | reset_cmd_coll(); 95 | pthread_mutex_unlock(&_mutex); 96 | } 97 | 98 | /* 99 | Dispatch commands with the interface name hint. Will be 100 | invoked on NEWLINK netlink event. 101 | */ 102 | void send_intf_cmds(const char *ifname, uint64_t *msg_seq) 103 | { 104 | if (!ifname) 105 | return; 106 | 107 | zlist_t *coll = get_resync_coll(); 108 | if (coll) { 109 | command_node_t *c; 110 | for (c = zlist_first(coll); c; c = zlist_next(coll)) 111 | /* only pub commands for iface */ 112 | if ((c->_node->_interface != NULL) && 113 | streq(ifname, c->_node->_interface)) 114 | if (publish_config_cmd(c, msg_seq) != 0) 115 | err("send interface command error"); 116 | } 117 | } 118 | 119 | /* 120 | Send single configuration command during resync 121 | operation. 122 | */ 123 | static int publish_resync_cmd(void *s, void *morestuff, bool send_db_key) 124 | { 125 | command_node_t *cmd = (command_node_t *) s; 126 | if (cmd == NULL || cmd->_node == NULL || morestuff == NULL) 127 | return -1; 128 | 129 | target_t *target = (target_t *) morestuff; 130 | 131 | if (zframe_send(&target->client, target->socket, 132 | ZFRAME_MORE + ZFRAME_REUSE)) { 133 | err("zframe_send failed: %s", strerror(errno)); 134 | return -1; 135 | } 136 | 137 | if (cmd->_node->_bin) { 138 | int bin_len; 139 | char *line = strdup(cmd->_node->_value); 140 | if (!line) 141 | panic("Memory allocation failure copying cmd line"); 142 | 143 | if (extract_protobuf((char **)&line, &bin_len) != 0) { 144 | err("Failure to publish binary"); 145 | free(line); 146 | return -1; 147 | } 148 | 149 | dbg("send binary [%"PRIu64"] topic '%s', (%d)", 150 | cmd->_node->_seq, 151 | "protobuf", bin_len); 152 | 153 | if (zstr_sendm(target->socket, PROTOBUF_TOPIC)) { 154 | err("zstr_sendm failed: %s", strerror(errno)); 155 | free(line); 156 | return -1; 157 | } 158 | 159 | /* FIXME racy on resync */ 160 | if (seqno_sendm(target->socket, cmd->_node->_seq)) { 161 | err("seqno_sendm failed: %s", strerror(errno)); 162 | free(line); 163 | return -1; 164 | } 165 | 166 | if (send_db_key) { 167 | if (zstr_sendm(target->socket, cmd->_db_key)) { 168 | err("zstr_sendm key failed: %s", 169 | strerror(errno)); 170 | return -1; 171 | } 172 | } 173 | 174 | /* just create a frame and send.. */ 175 | zframe_t *frame = zframe_new(line, bin_len); 176 | if (zframe_send(&frame, target->socket, 0)) { 177 | err("zframe_send failed: %s", strerror(errno)); 178 | zframe_destroy(&frame); 179 | free(line); 180 | return -1; 181 | } 182 | free(line); 183 | zframe_destroy(&frame); 184 | } else { 185 | dbg("send [%"PRIu64"] cmd '%s' '%s'", cmd->_node->_seq, 186 | cmd->_node->_topic, cmd->_node->_value); 187 | 188 | if (zstr_sendm(target->socket, cmd->_node->_topic)) { 189 | err("zstr_sendm failed: %s", strerror(errno)); 190 | return -1; 191 | } 192 | 193 | /* FIXME racy on resync */ 194 | if (seqno_sendm(target->socket, cmd->_node->_seq)) { 195 | err("seqno_sendm failed: %s", strerror(errno)); 196 | return -1; 197 | } 198 | 199 | if (send_db_key) { 200 | if (zstr_sendm(target->socket, cmd->_db_key)) { 201 | err("zstr_sendm key failed: %s", 202 | strerror(errno)); 203 | return -1; 204 | } 205 | } 206 | 207 | if (zstr_send(target->socket, cmd->_node->_value)) { 208 | err("zstr_send failed: %s", strerror(errno)); 209 | return -1; 210 | } 211 | } 212 | return 0; 213 | } 214 | 215 | /* 216 | Resync all configuration commands to requesting 217 | dataplane. 218 | */ 219 | void config_send(zsock_t *socket, zframe_t * to, bool send_db_key) 220 | { 221 | target_t t = { socket, to }; 222 | target_t *target = &t; 223 | 224 | pthread_mutex_lock(&_mutex); 225 | zlist_t *coll = get_resync_coll(); 226 | if (coll) { 227 | command_node_t *c; 228 | for (c = zlist_first(coll); c; c = zlist_next(coll)) 229 | if (!suppress_intf_cmd(c->_node->_interface)) 230 | publish_resync_cmd(c, target, send_db_key); 231 | } 232 | pthread_mutex_unlock(&_mutex); 233 | } 234 | -------------------------------------------------------------------------------- /daemon/configstore.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header supporting mostly interface between the generic configuration 3 | * store and the rest of the controller. The external requirements 4 | * are to interpret single commands and to provide a snapshot 5 | * of all commands during a resync request. 6 | * 7 | * Copyright (c) 2019-2021, AT&T Intellectual Property. All rights reserved. 8 | * Copyright (c) 2012-2017 by Brocade Communications Systems, Inc. 9 | * All rights reserved. 10 | * 11 | * SPDX-License-Identifier: LGPL-2.1-only 12 | * 13 | **/ 14 | #ifndef __CONFIGSTORE_H__ 15 | #define __CONFIGSTORE_H__ 16 | 17 | int config_cmd(const char *line); 18 | void send_cmds(uint64_t *msg_seq); 19 | void send_intf_cmds(const char *ifname, uint64_t *msg_seq); 20 | void config_send(zsock_t *socket, zframe_t * to, bool send_db_key); 21 | 22 | #endif /* __CONFIGSTORE_H__ */ 23 | -------------------------------------------------------------------------------- /daemon/controller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Controller parameters 3 | * 4 | * Copyright (c) 2017-2020 AT&T Intellectual Property. All rights reserved. 5 | * Copyright (c) 2012-2017 by Brocade Communications Systems, Inc. 6 | * All rights reserved. 7 | * 8 | * SPDX-License-Identifier: LGPL-2.1-only 9 | */ 10 | 11 | #ifdef RTNLGRP_RTDMN 12 | #include 13 | #endif 14 | #include 15 | #include "ip_addr.h" 16 | #include "vplane.h" 17 | #include "nlmsg.h" 18 | 19 | /* Used to silence Gcc where necessary */ 20 | #define __unused __attribute__((unused)) 21 | 22 | /* These are sanity check values, code should handle larger */ 23 | #define MAX_DATAPLANE_ID 1024 24 | #define MAX_PORTS 256 25 | 26 | /* command socket */ 27 | #define CMD_IPC "ipc:///var/run/vyatta/vplaned.socket" 28 | 29 | extern int debug; 30 | int zmsg_popu32(zmsg_t *msg, uint32_t *p); 31 | void request_thread(zsock_t *pipe, void *args); 32 | void request_test_msg(void *sock, zmsg_t *msg, void *snap); 33 | zsock_t *request_test_reconfigure(zloop_t *loop, zmsg_t *msg, void *pipe, 34 | zsock_t *request); 35 | void authentication_enable(zsock_t *socket); 36 | 37 | extern int udp_fd; 38 | #define panic(format, args...) __panic(__func__, format, ##args) 39 | void __panic(const char *funcname, const char *format, ...) 40 | __attribute__((noreturn)) 41 | __attribute__((format(printf, 2, 3))); 42 | void die(const char *format, ...) 43 | __attribute__((noreturn)) 44 | __attribute__((format(printf, 1, 2))); 45 | 46 | void logit(int level, char c, const char *format, ...) 47 | __attribute__((format(printf, 3, 4))); 48 | 49 | #define dbg(format, args...) \ 50 | do { \ 51 | if (debug) logit(LOG_DEBUG, 'D', format, ##args); \ 52 | } while(0) 53 | #define info(format, args...) logit(LOG_INFO, 'I', format, ##args) 54 | #define notice(format, args...) logit(LOG_NOTICE, 'N', format, ##args) 55 | #define err(format, args...) logit(LOG_ERR, 'E', format, ##args) 56 | 57 | struct nlattr; 58 | int link_attr(const struct nlattr *attr, void *data); 59 | int linkinfo_attr(const struct nlattr *attr, void *data); 60 | 61 | int if_get_flags(const char *ifname); 62 | int if_set_flags(const char *ifname, unsigned flags); 63 | struct ether_addr; 64 | int if_get_ethaddr(const char *ifname, struct ether_addr *eth); 65 | int if_set_ethaddr(const char *ifname, const struct ether_addr *eth); 66 | int if_rename(const char *oldname, const char *newname); 67 | 68 | /* Port management */ 69 | void port_init(void); 70 | void port_destroy(void); 71 | int port_create(const vplane_t *vp, uint32_t port, 72 | const char *ifname, const struct ether_addr *eth, 73 | const char *driver, const char *bus, 74 | unsigned int if_flags, unsigned int mtu, 75 | uint32_t *ifindex); 76 | int port_delete(const vplane_t *vp, uint32_t port); 77 | int port_state_change(const vplane_t *vp, uint32_t port, uint32_t ifindex, 78 | uint32_t operstate); 79 | int port_set_stats(const char *ifname, zframe_t *fr, bool aggregate, int dp_id); 80 | int port_set_speed(const char *ifname, unsigned speed, 81 | unsigned duplex, uint32_t advertised, 82 | bool preserve_link_modes); 83 | int assign_port(const char *json, unsigned int dp_id, 84 | char *ifname, struct ether_addr *eth, 85 | char **driver, char **bus, unsigned int *if_flags, 86 | unsigned int *mtu); 87 | void del_if_stats(const char *ifname); 88 | 89 | /* Tunnel management */ 90 | typedef struct mnl_socket tun_t; 91 | tun_t *tun_init(void); 92 | void tun_destroy(tun_t *); 93 | struct ether_addr; 94 | int tun_delete(tun_t *self, unsigned int ifindex); 95 | int tun_set_dormant(tun_t *nl, const char *ifname); 96 | int tun_set_linkstate(tun_t *self, unsigned int ifindex, 97 | unsigned int operstate); 98 | bool tun_admin_is_up(tun_t *self, unsigned int ifindex); 99 | void tun_admin_toggle(tun_t *self, unsigned int ifindex); 100 | 101 | const char *nl_route_type(unsigned int type); 102 | int nl_generate_topic(const struct nlmsghdr *nlm, char *buf, size_t len, 103 | uint32_t *ifindex); 104 | 105 | void nl_propagate_nlmsg(nlmsg_t *nmsg); 106 | 107 | void nl_publisher_lock(void); 108 | void nl_publisher_unlock(void); 109 | 110 | const char *nlmsg_type_name_rtnl(const struct nlmsghdr *nlm); 111 | 112 | typedef struct _snapshot snapshot_t; 113 | snapshot_t *snapshot_new(void); 114 | void snapshot_destroy(snapshot_t **snap); 115 | uint64_t snapshot_seqno(const snapshot_t *snap); 116 | int snapshot_update(snapshot_t *snap, nlmsg_t *nmsg); 117 | void snapshot_send(snapshot_t *snap, void *socket, zframe_t *to); 118 | 119 | /* Config */ 120 | void publish_cmd(const char *topic, uint64_t seqno, const char *line, bool bin); 121 | int nl_generate_topic_xfrm(const struct nlmsghdr *nlh, char *buf, 122 | size_t buflen, bool *snapshot); 123 | int nl_generate_topic_l2tp(const struct nlmsghdr *nlh, char *buf, 124 | size_t buflen); 125 | void set_perm(const char *path); 126 | int process_gen_config(const char *line); 127 | 128 | /* Hwbinding */ 129 | const char *get_name_by_pcislot(int slot, int function); 130 | const char *get_name_by_pciaddr(const char *pciaddress); 131 | const char *get_name_by_mac(const char *mac); 132 | const char *get_name_by_fwidx(int fwidx); 133 | const char *get_name_by_port(int port); 134 | void read_interface_cfg(const char *name); 135 | void interface_cfg_destroy(void); 136 | 137 | int fsnotify_init(void); 138 | void fsnotify_handle_events(void); 139 | void fsnotify_destroy(void); 140 | void fsnotify_add_mpls_watchers(void); 141 | void fsnotify_add_redirects_watchers(void); 142 | -------------------------------------------------------------------------------- /daemon/hwbinding.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "controller.h" 21 | 22 | static zlist_t *hwb_list; 23 | static char *interface_cfg; 24 | static time_t interface_cfg_mtime; 25 | 26 | enum binding_type { 27 | HWBINDING_FWIDX = 1, /* firmware index */ 28 | HWBINDING_MACADDR, /* mac address */ 29 | HWBINDING_PCIADDR, /* pci address */ 30 | HWBINDING_PCISLOT, /* pci slot */ 31 | HWBINDING_PORT, /* dpdk port */ 32 | }; 33 | 34 | #define PCI_SCAN_FMT "%hx:%hhx:%hhx.%hhx" 35 | #define PCI_SCAN_FMT_SHORT "%hhx:%hhx.%hhx" 36 | 37 | struct hwbinding { 38 | enum binding_type type; 39 | union { 40 | int fwidx; 41 | int port; 42 | struct pcislot { 43 | int slot; 44 | int function; 45 | } pcislot; 46 | struct pciaddr { 47 | unsigned short domain; 48 | unsigned char bus; 49 | unsigned char devid; 50 | unsigned char function; 51 | } pciaddr; 52 | struct ether_addr eaddr; 53 | } u; 54 | char *name; 55 | }; 56 | 57 | static void free_hwbinding(void *data) 58 | { 59 | struct hwbinding *hwb = data; 60 | 61 | free(hwb->name); 62 | free(hwb); 63 | } 64 | 65 | static enum binding_type name_to_binding_type(char *name) 66 | { 67 | if (!strcmp(name, "pci-address")) 68 | return HWBINDING_PCIADDR; 69 | if (!strcmp(name, "pci-slot")) 70 | return HWBINDING_PCISLOT; 71 | if (!strcmp(name, "firmware-index")) 72 | return HWBINDING_FWIDX; 73 | if (!strcmp(name, "mac")) 74 | return HWBINDING_MACADDR; 75 | if (!strcmp(name, "port")) 76 | return HWBINDING_PORT; 77 | 78 | return 0; 79 | } 80 | 81 | static void parse_interface_cfg(const char *config) 82 | { 83 | FILE *fp; 84 | char line[BUFSIZ]; 85 | char name[BUFSIZ], type[BUFSIZ], value[BUFSIZ]; 86 | struct hwbinding *hwb; 87 | int lineno = 0; 88 | char *p; 89 | 90 | if (!hwb_list) { 91 | hwb_list = zlist_new(); 92 | if (!hwb_list) { 93 | err("%s: zlist_new() failed!", __func__); 94 | return; 95 | } 96 | } 97 | zlist_purge(hwb_list); 98 | 99 | fp = fopen(config, "r"); 100 | if (!fp) 101 | return; 102 | 103 | while (fgets(line, sizeof(line), fp) != NULL) { 104 | ++lineno; 105 | 106 | p = strchr(line, '#'); 107 | if (p) 108 | *p = '\0'; 109 | 110 | if (sscanf(line, "%s %s %s", name, type, value) != 3) 111 | continue; 112 | 113 | if (!isalpha(name[0])) 114 | goto fail; 115 | 116 | hwb = malloc(sizeof(*hwb)); 117 | if (!hwb) 118 | continue; 119 | memset(hwb, 0, sizeof(*hwb)); 120 | hwb->type = name_to_binding_type(type); 121 | if (!hwb->type) 122 | goto fail_free; 123 | if (strlen(name) >= IFNAMSIZ) 124 | goto fail_free; 125 | hwb->name = strdup(name); 126 | if (!hwb->name) 127 | goto fail_free; 128 | 129 | if (hwb->type == HWBINDING_FWIDX) { 130 | hwb->u.fwidx = atoi(value); 131 | } else if (hwb->type == HWBINDING_PORT) { 132 | hwb->u.port = atoi(value); 133 | } else if (hwb->type == HWBINDING_MACADDR) { 134 | struct ether_addr *eaddr; 135 | 136 | eaddr = ether_aton_r(value, &hwb->u.eaddr); 137 | if (!eaddr) 138 | goto fail_free; 139 | } else if (hwb->type == HWBINDING_PCISLOT) { 140 | int slot, function; 141 | 142 | if (sscanf(value, "%d.%d", &slot, &function) == 2) { 143 | hwb->u.pcislot.slot = slot; 144 | hwb->u.pcislot.function = function; 145 | } else { 146 | hwb->u.pcislot.slot = atoi(value); 147 | hwb->u.pcislot.function = 0; 148 | } 149 | } else if (hwb->type == HWBINDING_PCIADDR) { 150 | if (sscanf(value, PCI_SCAN_FMT, 151 | &hwb->u.pciaddr.domain, 152 | &hwb->u.pciaddr.bus, 153 | &hwb->u.pciaddr.devid, 154 | &hwb->u.pciaddr.function) != 4) { 155 | 156 | hwb->u.pciaddr.domain = 0; 157 | if (sscanf(value, PCI_SCAN_FMT_SHORT, 158 | &hwb->u.pciaddr.bus, 159 | &hwb->u.pciaddr.devid, 160 | &hwb->u.pciaddr.function) != 3) 161 | goto fail_free; 162 | } 163 | } 164 | 165 | if (zlist_append(hwb_list, hwb) < 0) { 166 | err("failed to add %s from %s", name, interface_cfg); 167 | free_hwbinding(hwb); 168 | continue; 169 | } 170 | zlist_freefn(hwb_list, hwb, free_hwbinding, false); 171 | continue; 172 | 173 | fail_free: 174 | free_hwbinding(hwb); 175 | fail: 176 | err("can't parse line %d in %s", lineno, interface_cfg); 177 | } 178 | fclose(fp); 179 | } 180 | 181 | void interface_cfg_destroy(void) 182 | { 183 | if (interface_cfg) { 184 | free(interface_cfg); 185 | interface_cfg_mtime = 0; 186 | interface_cfg = NULL; 187 | } 188 | 189 | if (hwb_list) { 190 | zlist_purge(hwb_list); 191 | zlist_destroy(&hwb_list); 192 | hwb_list = NULL; 193 | } 194 | } 195 | 196 | static void reread_interface_cfg(void) 197 | { 198 | struct stat stat_buf; 199 | 200 | if (!interface_cfg) 201 | return; 202 | 203 | if (stat(interface_cfg, &stat_buf) == -1) { 204 | if (hwb_list) 205 | zlist_purge(hwb_list); 206 | interface_cfg_mtime = 0; 207 | return; 208 | } 209 | 210 | if (interface_cfg_mtime != stat_buf.st_mtime) { 211 | interface_cfg_mtime = stat_buf.st_mtime; 212 | 213 | parse_interface_cfg(interface_cfg); 214 | } 215 | } 216 | 217 | void read_interface_cfg(const char *name) 218 | { 219 | interface_cfg_destroy(); 220 | interface_cfg = strdup(name); 221 | 222 | reread_interface_cfg(); 223 | } 224 | 225 | static const char *get_name_by_hwb(struct hwbinding *target) 226 | { 227 | struct hwbinding *hwb; 228 | 229 | reread_interface_cfg(); 230 | 231 | if (!hwb_list) 232 | return NULL; 233 | 234 | for (hwb = zlist_first(hwb_list); hwb; hwb = zlist_next(hwb_list)) { 235 | if (memcmp(hwb, target, 236 | sizeof(hwb->type) + sizeof(hwb->u)) == 0) 237 | return hwb->name; 238 | } 239 | 240 | return NULL; 241 | } 242 | 243 | const char *get_name_by_pcislot(int slot, int function) 244 | { 245 | struct hwbinding hw; 246 | 247 | memset(&hw, 0, sizeof(hw)); 248 | hw.type = HWBINDING_PCISLOT; 249 | hw.u.pcislot.slot = slot; 250 | hw.u.pcislot.function = function; 251 | 252 | return get_name_by_hwb(&hw); 253 | } 254 | 255 | const char *get_name_by_pciaddr(const char *pci_addr) 256 | { 257 | struct hwbinding hw; 258 | 259 | if (!pci_addr) 260 | return NULL; 261 | 262 | memset(&hw, 0, sizeof(hw)); 263 | hw.type = HWBINDING_PCIADDR; 264 | 265 | if (sscanf(pci_addr, PCI_SCAN_FMT, 266 | &hw.u.pciaddr.domain, 267 | &hw.u.pciaddr.bus, 268 | &hw.u.pciaddr.devid, 269 | &hw.u.pciaddr.function) != 4) 270 | return NULL; 271 | 272 | return get_name_by_hwb(&hw); 273 | } 274 | 275 | const char *get_name_by_mac(const char *mac) 276 | { 277 | struct hwbinding hw; 278 | 279 | if (!mac) 280 | return NULL; 281 | 282 | memset(&hw, 0, sizeof(hw)); 283 | hw.type = HWBINDING_MACADDR; 284 | if (!ether_aton_r(mac, &hw.u.eaddr)) 285 | return NULL; 286 | 287 | return get_name_by_hwb(&hw); 288 | } 289 | 290 | const char *get_name_by_fwidx(int fwidx) 291 | { 292 | struct hwbinding hw; 293 | 294 | memset(&hw, 0, sizeof(hw)); 295 | hw.type = HWBINDING_FWIDX; 296 | hw.u.fwidx = fwidx; 297 | 298 | return get_name_by_hwb(&hw); 299 | } 300 | 301 | const char *get_name_by_port(int port) 302 | { 303 | struct hwbinding hw; 304 | 305 | memset(&hw, 0, sizeof(hw)); 306 | hw.type = HWBINDING_PORT; 307 | hw.u.port = port; 308 | 309 | return get_name_by_hwb(&hw); 310 | } 311 | -------------------------------------------------------------------------------- /daemon/ip_addr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Address related data structures and functions. 9 | */ 10 | #if !defined(__ip_addr_h__) 11 | #define __ip_addr_h__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | /* Generic address */ 18 | struct ip_addr { 19 | sa_family_t af; 20 | union { 21 | struct in_addr v4; 22 | struct in6_addr v6; 23 | } ip; 24 | }; 25 | 26 | /** 27 | * Compares the passed address with local sock address. 28 | * returns 0 if address is same, -1 otherwise 29 | */ 30 | static inline int 31 | addr_cmp(const struct ip_addr *addr1, const struct ip_addr *addr2) 32 | { 33 | if ((addr1 == NULL) || (addr2 == NULL) || 34 | (addr1->af != addr2->af)) 35 | return -1; 36 | if (addr1->af == AF_INET) { 37 | if (addr1->ip.v4.s_addr == addr2->ip.v4.s_addr) 38 | return 0; 39 | return -1; 40 | } 41 | if (addr1->af == AF_INET6) 42 | return memcmp(&addr1->ip.v6, &addr2->ip.v6, 43 | sizeof(struct in6_addr)); 44 | return -1; 45 | } 46 | 47 | /** 48 | * Converts string to ip_addr 49 | */ 50 | static inline bool ip_addr_from_str(struct ip_addr *addr, const char *addrstr) 51 | { 52 | int rc; 53 | 54 | if (!addrstr || !addr) 55 | return false; 56 | 57 | rc = inet_pton(AF_INET, addrstr, &addr->ip.v4); 58 | if (rc == 1) { 59 | addr->af = AF_INET; 60 | return true; 61 | } 62 | rc = inet_pton(AF_INET6, addrstr, &addr->ip.v6); 63 | if (rc == 1) { 64 | addr->af = AF_INET6; 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /daemon/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | daemon_sources = files( 5 | 'bstr.c', 6 | 'configcmd.c', 7 | 'configdb.c', 8 | 'configstore.c', 9 | 'devname.c', 10 | 'fsnotify.c', 11 | 'hwbinding.c', 12 | 'main.c', 13 | 'mnlutil.c', 14 | 'mrtstat.c', 15 | 'nlmsg.c', 16 | 'parser.c', 17 | 'port.c', 18 | 'protobuf.c', 19 | 'request.c', 20 | 'snapshot.c', 21 | 'team.c', 22 | 'topic.c', 23 | 'tunnel.c', 24 | 'util.c', 25 | 'vplane.c' 26 | ) 27 | 28 | executable( 29 | 'vplaned', 30 | sources: [daemon_sources, controller_protobuf_c], 31 | dependencies: [ 32 | b64_dep, 33 | ini_dep, 34 | czmq_dep, 35 | json_dep, 36 | mnl_dep, 37 | vrfmanager_dep, 38 | systemd_dep, 39 | zmq_dep, 40 | proto_c_dep, 41 | jemalloc_dep, 42 | ], 43 | install: true, 44 | install_dir : '/opt/vyatta/sbin' 45 | ) 46 | -------------------------------------------------------------------------------- /daemon/mnlutil.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MNL (netlink) utility functions 3 | * 4 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 5 | * Copyright (c) 2016-2017 by Brocade Communications Systems, Inc. 6 | * All rights reserved. 7 | * 8 | * SPDX-License-Identifier: LGPL-2.1-only 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include "controller.h" 17 | 18 | int link_attr(const struct nlattr *attr, void *data) 19 | { 20 | const struct nlattr **tb = data; 21 | int type = mnl_attr_get_type(attr); 22 | 23 | /* skip unsupported attribute in user-space */ 24 | if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) 25 | return MNL_CB_OK; 26 | 27 | switch (type) { 28 | case IFLA_MTU: 29 | case IFLA_LINK: 30 | case IFLA_MASTER: 31 | if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { 32 | notice("link attribute %d not u32", type); 33 | return MNL_CB_ERROR; 34 | } 35 | break; 36 | 37 | case IFLA_IFNAME: 38 | if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { 39 | notice("link ifname not a valid string"); 40 | return MNL_CB_ERROR; 41 | } 42 | break; 43 | } 44 | 45 | tb[type] = attr; 46 | return MNL_CB_OK; 47 | } 48 | 49 | int linkinfo_attr(const struct nlattr *attr, void *data) 50 | { 51 | const struct nlattr **tb = data; 52 | int type = mnl_attr_get_type(attr); 53 | 54 | /* skip unsupported attribute in user-space */ 55 | if (mnl_attr_type_valid(attr, IFLA_INFO_MAX) < 0) 56 | return MNL_CB_OK; 57 | 58 | if (type == IFLA_INFO_KIND) { 59 | if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { 60 | err("invalid link info kind %d: %s\n", type, 61 | strerror(errno)); 62 | return MNL_CB_ERROR; 63 | } 64 | } 65 | 66 | tb[type] = attr; 67 | return MNL_CB_OK; 68 | } 69 | 70 | const char *nlmsg_type_name_rtnl(const struct nlmsghdr *nlh) 71 | { 72 | static char buf[32]; 73 | 74 | switch (nlh->nlmsg_type) { 75 | case RTM_NEWLINK: return "NEWLINK"; 76 | case RTM_DELLINK: return "DELLINK"; 77 | case RTM_GETLINK: return "GETLINK"; 78 | case RTM_SETLINK: return "SETLINK"; 79 | case RTM_NEWADDR: return "NEWADDR"; 80 | case RTM_DELADDR: return "DELADDR"; 81 | case RTM_GETADDR: return "GETADDR"; 82 | case RTM_NEWROUTE: return "NEWROUTE"; 83 | case RTM_DELROUTE: return "DELROUTE"; 84 | case RTM_GETROUTE: return "GETROUTE"; 85 | case RTM_NEWNEIGH: return "NEWNEIGH"; 86 | case RTM_DELNEIGH: return "DELNEIGH"; 87 | case RTM_GETNEIGH: return "GETNEIGH"; 88 | case RTM_NEWRULE: return "NEWRULE"; 89 | case RTM_DELRULE: return "DELRULE"; 90 | case RTM_GETRULE: return "GETRULE"; 91 | case RTM_NEWQDISC: return "NEWQDISC"; 92 | case RTM_DELQDISC: return "DELQDISC"; 93 | case RTM_GETQDISC: return "GETQDISC"; 94 | case RTM_NEWTCLASS: return "NEWTCLASS"; 95 | case RTM_DELTCLASS: return "DELTCLASS"; 96 | case RTM_GETTCLASS: return "GETTCLASS"; 97 | case RTM_NEWTFILTER: return "NEWTFILTER"; 98 | case RTM_DELTFILTER: return "DELTFILTER"; 99 | case RTM_GETTFILTER: return "GETTFILTER"; 100 | case RTM_NEWACTION: return "NEWACTION"; 101 | case RTM_DELACTION: return "DELACTION"; 102 | case RTM_GETACTION: return "GETACTION"; 103 | case RTM_NEWPREFIX: return "NEWPREFIX"; 104 | case RTM_GETMULTICAST: return "GETMULTICAST"; 105 | case RTM_GETANYCAST: return "GETANYCAST"; 106 | case RTM_NEWNEIGHTBL: return "NEWNEIGHTBL"; 107 | case RTM_GETNEIGHTBL: return "GETNEIGHTBL"; 108 | case RTM_SETNEIGHTBL: return "SETNEIGHTBL"; 109 | case RTM_NEWNDUSEROPT: return "NEWNDUSEROPT"; 110 | case RTM_NEWADDRLABEL: return "NEWADDRLABEL"; 111 | case RTM_DELADDRLABEL: return "DELADDRLABEL"; 112 | case RTM_GETADDRLABEL: return "GETADDRLABEL"; 113 | case RTM_GETDCB: return "GETDCB"; 114 | case RTM_SETDCB: return "SETDCB"; 115 | case RTM_NEWNETCONF: return "NEWNETCONF"; 116 | case RTM_DELNETCONF: return "DELNETCONF"; 117 | case RTM_GETNETCONF: return "GETNETCONF"; 118 | case RTM_NEWMDB: return "NEWMDB"; 119 | case RTM_DELMDB: return "DELMDB"; 120 | case RTM_GETMDB: return "GETMDB"; 121 | #ifdef RTNLGRP_RTDMN 122 | case RTM_NEWRTDMN: return "NEWRTDMN"; 123 | case RTM_DELRTDMN: return "DELRTDMN"; 124 | #endif 125 | case RTM_NEWCHAIN: return "NEWCHAIN"; 126 | case RTM_DELCHAIN: return "DELCHAIN"; 127 | default: 128 | break; 129 | } 130 | 131 | snprintf(buf, sizeof(buf), "RTNL %u", nlh->nlmsg_type); 132 | return buf; 133 | } 134 | -------------------------------------------------------------------------------- /daemon/mrtstat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2014-2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | 9 | /* 10 | * Multicast packet stats 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #ifndef SO_RTDOMAIN 23 | #include 24 | #endif 25 | 26 | #include 27 | #include "compat.h" 28 | #include "controller.h" 29 | #include "mrtstat.h" 30 | 31 | /* 32 | * Size of buffer to hold VRF ID as a string. 33 | * Max 10 digits (32 bit VRF IDs) + 1 digit (terminating "\0") 34 | */ 35 | #define MAX_MRT_HASH_KEY_LEN 11 36 | 37 | /* 38 | * Hash tables to hold per-VRF IGMP sockets for each AF. 39 | * Key is VRF ID (converted to a string). 40 | */ 41 | static zhash_t *igmpv4_sockets_hash_table; 42 | static zhash_t *igmpv6_sockets_hash_table; 43 | 44 | /* 45 | * Create hash tables at initialisation time. 46 | */ 47 | void igmp_setup(void) 48 | { 49 | igmpv4_sockets_hash_table = zhash_new(); 50 | igmpv6_sockets_hash_table = zhash_new(); 51 | 52 | if (!igmpv4_sockets_hash_table || !igmpv6_sockets_hash_table) 53 | die("Mcast stats socket creation failure\n"); 54 | } 55 | 56 | /* 57 | * Destroying a hash table results in close_igmp_socket() 58 | * being executed for every hash table item (i.e. socket). 59 | * close_igmp_socket() closes the socket and frees the 60 | * associated memory. 61 | */ 62 | void igmp_teardown(void) 63 | { 64 | zhash_destroy(&igmpv4_sockets_hash_table); 65 | zhash_destroy(&igmpv6_sockets_hash_table); 66 | } 67 | 68 | /* 69 | * Callback invoked when item deleted from IPv4/IPv6 hash tables. 70 | */ 71 | static void close_igmp_socket(void *arg) 72 | { 73 | int *igmp_socket = arg; 74 | close(*igmp_socket); 75 | free(igmp_socket); 76 | } 77 | 78 | /* 79 | * IPv4/IPv6 hash key is simply VRF ID converted into a string. 80 | */ 81 | static char *igmp_socket_hash_key(uint32_t vrf_id, char *key_buf, 82 | uint32_t buf_size) 83 | { 84 | uint32_t written; 85 | written = snprintf(key_buf, buf_size, "%d", vrf_id); 86 | if (written >= buf_size) 87 | return NULL; 88 | 89 | return key_buf; 90 | } 91 | 92 | 93 | static int igmp_socket_bind_vrf(int socket, uint32_t vrf_id, uint32_t af) 94 | { 95 | if (vrf_id == VRF_DEFAULT_ID) 96 | return 0; 97 | 98 | #ifdef SO_RTDOMAIN 99 | if (setsockopt (socket, SOL_SOCKET, SO_RTDOMAIN, 100 | &vrf_id, sizeof (vrf_id)) < 0) { 101 | err("Failure setting socket %d into VRF %u; err = %s", 102 | socket, vrf_id, strerror(errno)); 103 | return -1; 104 | } 105 | #else 106 | char *vrf_name; 107 | uint32_t kernel_table_id; 108 | 109 | if (!get_vrf_name (vrf_id, &vrf_name)) { 110 | err("Failure getting name for VRF %u", vrf_id); 111 | return -1; 112 | } 113 | 114 | kernel_table_id = get_vrf_kernel_table_id (vrf_name, RT_TABLE_MAIN); 115 | free(vrf_name); 116 | 117 | if (kernel_table_id == RT_TABLE_UNSPEC) { 118 | err("Failure getting kernel table ID for VRF %u", vrf_id); 119 | return -1; 120 | } 121 | 122 | if (setsockopt (socket, af == AF_INET ? IPPROTO_IP : IPPROTO_IPV6, 123 | af == AF_INET ? MRT_TABLE : MRT6_TABLE, 124 | &kernel_table_id, sizeof(kernel_table_id)) < 0) { 125 | err("Failure setting kernel table ID %u for VRF %u on socket %d" 126 | "; err = %s", kernel_table_id, vrf_id, socket, strerror(errno)); 127 | return -1; 128 | } 129 | #endif 130 | 131 | return 0; 132 | } 133 | 134 | /* 135 | * Create a new socket to deliver stats update from data plane to the kernel, 136 | * using the sockets options to ensure stats associated with correct VRF. 137 | * This socket will cached in an AF-specific hash table (key is VRF ID) 138 | * for use in subsequent stats updates. 139 | */ 140 | static int init_igmp_socket(uint32_t vrf_id, uint32_t af, uint32_t protocol) 141 | { 142 | int igmp_socket; 143 | 144 | if ((igmp_socket = socket(af, SOCK_RAW, protocol)) < 0) 145 | return -1; 146 | 147 | if (igmp_socket_bind_vrf(igmp_socket, vrf_id, af) < 0) { 148 | close(igmp_socket); 149 | return -1; 150 | } 151 | 152 | return igmp_socket; 153 | } 154 | 155 | /* 156 | * Flag in stats message indicates this stats block is for an mroute 157 | * which is the last one in the VRF and is about to be deleted. Thus 158 | * the corresponding per-VRF socket should be closed. 159 | */ 160 | int mcast_close_stats_socket(uint32_t vrf_id, uint32_t af) 161 | { 162 | const char *key; 163 | char key_buf[MAX_MRT_HASH_KEY_LEN]; 164 | zhash_t *sockets_hash_table; 165 | int *igmp_socket; 166 | 167 | switch (af) { 168 | case AF_INET: 169 | sockets_hash_table = igmpv4_sockets_hash_table; 170 | break; 171 | case AF_INET6: 172 | sockets_hash_table = igmpv6_sockets_hash_table; 173 | break; 174 | default: 175 | return -1; 176 | } 177 | 178 | key = igmp_socket_hash_key(vrf_id, key_buf, sizeof(key_buf)); 179 | if (!key) 180 | return -1; 181 | 182 | igmp_socket = zhash_lookup(sockets_hash_table, key); 183 | if (!igmp_socket) { 184 | notice("%s mcast socket for VRF %u already closed", 185 | (af == AF_INET) ? "IPv4" : "IPv6", vrf_id); 186 | return 0; 187 | } 188 | 189 | zhash_delete(sockets_hash_table, key); 190 | return 0; 191 | } 192 | 193 | /* 194 | * Stats update from data plane received for specific IPv4 VRF. 195 | * Need to find or create the socket and deliver the stats to the 196 | * kernel via an ioctl call. 197 | */ 198 | int set_sg_count(struct sioc_sg_req *sgreq, uint32_t vrf_id) 199 | { 200 | int *igmpv4_socket; 201 | const char *key; 202 | char key_buf[MAX_MRT_HASH_KEY_LEN]; 203 | char source[INET_ADDRSTRLEN]; 204 | char group[INET_ADDRSTRLEN]; 205 | 206 | key = igmp_socket_hash_key(vrf_id, key_buf, sizeof(key_buf)); 207 | if (!key) 208 | return 1; 209 | 210 | igmpv4_socket = zhash_lookup(igmpv4_sockets_hash_table, key); 211 | if (!igmpv4_socket) { 212 | igmpv4_socket = calloc(1, sizeof(int)); 213 | if (!igmpv4_socket) 214 | return 1; 215 | 216 | inet_ntop(AF_INET, &sgreq->src, source, sizeof(source)); 217 | inet_ntop(AF_INET, &sgreq->grp, group, sizeof(group)); 218 | notice("First stats update for IPv4 VRF %u; mroute is (%s, %s)", 219 | vrf_id, source, group); 220 | 221 | if ((*igmpv4_socket = init_igmp_socket(vrf_id, AF_INET, 222 | IPPROTO_IGMP)) < 0) { 223 | err("Failure opening socket for IPv4 VRF %u", vrf_id); 224 | free(igmpv4_socket); 225 | return 1; 226 | } 227 | 228 | if (zhash_insert(igmpv4_sockets_hash_table, 229 | key, igmpv4_socket) < 0) { 230 | err("Failure caching socket for IPv4 VRF %u", vrf_id); 231 | close_igmp_socket(igmpv4_socket); 232 | return 1; 233 | } 234 | 235 | zhash_freefn(igmpv4_sockets_hash_table, key, close_igmp_socket); 236 | } 237 | 238 | if (ioctl(*igmpv4_socket, SIOCSETSGCNT, (char *)sgreq) < 0) { 239 | err("SIOCSETSGCNT on socket %d failure; err = %s", 240 | *igmpv4_socket, strerror(errno)); 241 | zhash_delete(igmpv4_sockets_hash_table, key); 242 | return 1; 243 | } 244 | 245 | return 0; 246 | } 247 | 248 | /* 249 | * Stats update from data plane received for specific IPv6 VRF. 250 | * Need to find or create the socket and deliver the stats to the 251 | * kernel via an ioctl call. 252 | */ 253 | int set_sg6_count(struct sioc_sg_req6 *sgreq, uint32_t vrf_id) 254 | { 255 | int *igmpv6_socket; 256 | const char *key; 257 | char key_buf[MAX_MRT_HASH_KEY_LEN]; 258 | char source[INET6_ADDRSTRLEN]; 259 | char group[INET6_ADDRSTRLEN]; 260 | 261 | key = igmp_socket_hash_key(vrf_id, key_buf, sizeof(key_buf)); 262 | if (!key) 263 | return 1; 264 | 265 | igmpv6_socket = zhash_lookup(igmpv6_sockets_hash_table, key); 266 | if (!igmpv6_socket) { 267 | igmpv6_socket = calloc(1, sizeof(int)); 268 | if (!igmpv6_socket) 269 | return 1; 270 | 271 | inet_ntop(AF_INET6, &sgreq->src.sin6_addr, 272 | source, sizeof(source)); 273 | inet_ntop(AF_INET6, &sgreq->grp.sin6_addr, 274 | group, sizeof(group)); 275 | notice("First stats update for IPv6 VRF %u; mroute is (%s, %s)", 276 | vrf_id, source, group); 277 | 278 | if ((*igmpv6_socket = init_igmp_socket(vrf_id, AF_INET6, 279 | IPPROTO_ICMPV6)) < 0) { 280 | err("Failure opening socket for IPv6 VRF %u", vrf_id); 281 | free(igmpv6_socket); 282 | return 1; 283 | } 284 | 285 | if (zhash_insert(igmpv6_sockets_hash_table, key, 286 | igmpv6_socket) < 0) { 287 | err("Failure caching socket for IPv6 VRF %u", vrf_id); 288 | close_igmp_socket(igmpv6_socket); 289 | return 1; 290 | } 291 | 292 | zhash_freefn(igmpv6_sockets_hash_table, key, close_igmp_socket); 293 | } 294 | 295 | if (ioctl(*igmpv6_socket, SIOCSETSGCNT_IN6, (char *)sgreq) < 0) { 296 | err("SIOCSETSGCNT_IN6 on socket %d failure; err = %s", 297 | *igmpv6_socket, strerror(errno)); 298 | zhash_delete(igmpv6_sockets_hash_table, key); 299 | return 1; 300 | } 301 | 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /daemon/mrtstat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2014-2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | #ifndef _MRTSTAT_H 9 | #define _MRTSTAT_H 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | /* 16 | * Multicast packet stats 17 | */ 18 | 19 | #define SIOCSETSGCNT (SIOCPROTOPRIVATE+3) 20 | #define SIOCSETSGCNT_IN6 (SIOCPROTOPRIVATE+3) 21 | 22 | int set_sg_count(struct sioc_sg_req *sgreq, uint32_t vrf_id); 23 | int set_sg6_count(struct sioc_sg_req6 *sgreq, uint32_t vrf_id); 24 | int mcast_close_stats_socket(uint32_t vrf_id, uint32_t af); 25 | 26 | void igmp_setup(void); 27 | void igmp_teardown(void); 28 | #endif 29 | -------------------------------------------------------------------------------- /daemon/nlmsg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * netlink message handling functions 3 | * 4 | * Copyright (c) 2017-2020, AT&T Intellectual Property. All rights reserved. 5 | * Copyright (c) 2017 by Brocade Communications Systems, Inc. 6 | * All rights reserved. 7 | * 8 | * SPDX-License-Identifier: LGPL-2.1-only 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "controller.h" 18 | #include "nlmsg.h" 19 | 20 | struct _nlmsg { 21 | atomic_int refcnt; 22 | char *topic; 23 | uint64_t seqno; 24 | uint32_t ifindex; 25 | 26 | size_t size; /* number of bytes of data */ 27 | byte data[]; /* variable length */ 28 | }; 29 | 30 | /* 31 | * Construct a netlink message object from 32 | */ 33 | nlmsg_t *nlmsg_new(const char *str, uint64_t seqno, 34 | const void *data, size_t len) 35 | { 36 | nlmsg_t *self = malloc(sizeof(*self) + len); 37 | if (!self) 38 | return NULL; 39 | 40 | atomic_store_explicit(&self->refcnt, 1, memory_order_relaxed); 41 | self->topic = strdup(str); 42 | self->seqno = seqno; 43 | self->ifindex = 0; 44 | self->size = len; 45 | memcpy(self->data, data, len); 46 | 47 | return self; 48 | } 49 | 50 | void nlmsg_free(nlmsg_t *self) 51 | { 52 | assert(atomic_load_explicit(&self->refcnt, memory_order_relaxed) != 0); 53 | 54 | int old_refcnt = atomic_fetch_sub_explicit(&self->refcnt, 1, 55 | memory_order_relaxed); 56 | if (old_refcnt <= 1) { 57 | free(self->topic); 58 | free(self); 59 | } 60 | } 61 | 62 | static 63 | void zmq_nlmsg_free(void *data __unused, void *hint) 64 | { 65 | nlmsg_t *self = hint; 66 | nlmsg_free(self); 67 | } 68 | 69 | nlmsg_t *nlmsg_copy(nlmsg_t *self) 70 | { 71 | atomic_fetch_add_explicit(&self->refcnt, 1, memory_order_relaxed); 72 | return self; 73 | } 74 | 75 | int seqno_sendm(zsock_t *socket, uint64_t seqno) 76 | { 77 | zmsg_t *m = zmsg_new(); 78 | if (zmsg_addmem(m, &seqno, sizeof(uint64_t)) == -1) 79 | return -1; 80 | 81 | if (zmsg_sendm(&m, socket) == -1) 82 | return -1; 83 | 84 | return 0; 85 | } 86 | 87 | /* 88 | * Constructor from incoming message 89 | * received protocol with three parts: 90 | * [1] topic string - extracted and pass-in by caller. 91 | * [2] sequence number 92 | * [3] netlink data 93 | */ 94 | nlmsg_t *nlmsg_recv(const char *topic, zmsg_t *msg) 95 | { 96 | nlmsg_t *nmsg = NULL; 97 | zframe_t *frame = zmsg_first(msg); 98 | if (!frame || zframe_size(frame) != sizeof(uint64_t)) 99 | return NULL; 100 | 101 | uint64_t seqno; 102 | memcpy(&seqno, zframe_data(frame), sizeof(uint64_t)); 103 | 104 | frame = zmsg_next(msg); 105 | if (frame != NULL) 106 | nmsg = nlmsg_new(topic, seqno, 107 | zframe_data(frame), zframe_size(frame)); 108 | 109 | return nmsg; 110 | } 111 | 112 | /* Send topic/seqno/netlink. 113 | If successful then destroyed after sending. */ 114 | int nlmsg_send(nlmsg_t *self, zsock_t *socket) 115 | { 116 | zmq_msg_t message; 117 | void *handle = zsock_resolve(socket); 118 | int rc; 119 | 120 | rc = zmq_msg_init_data(&message, 121 | self->topic, strlen(self->topic), 122 | NULL, NULL); 123 | if (rc) 124 | goto err; 125 | 126 | if (zmq_msg_send(&message, handle, ZMQ_SNDMORE) == -1) { 127 | rc = -1; 128 | zmq_msg_close(&message); 129 | goto err; 130 | } 131 | 132 | rc = zmq_msg_init_data(&message, 133 | &self->seqno, sizeof(uint64_t), 134 | NULL, NULL); 135 | if (rc) 136 | goto err; 137 | 138 | if (zmq_msg_send(&message, handle, ZMQ_SNDMORE) == -1) { 139 | rc = -1; 140 | zmq_msg_close(&message); 141 | goto err; 142 | } 143 | 144 | rc = zmq_msg_init_data(&message, 145 | self->data, self->size, 146 | zmq_nlmsg_free, self); 147 | if (rc) 148 | goto err; 149 | nlmsg_copy(self); 150 | 151 | if (zmq_msg_send(&message, handle, 0) == -1) { 152 | rc = -1; 153 | zmq_msg_close(&message); 154 | goto err; 155 | } 156 | 157 | err: 158 | nlmsg_free(self); 159 | return rc; 160 | } 161 | 162 | void nlmsg_dump(const char *prefix, const nlmsg_t *self) 163 | { 164 | unsigned l = 0; 165 | char buf[BUFSIZ]; 166 | 167 | if (!self) 168 | return; 169 | 170 | if (prefix) { 171 | if (debug > 2) 172 | l = snprintf(buf, sizeof(buf), "--- %s ---\n", prefix); 173 | else 174 | l = snprintf(buf, sizeof(buf), "%s ", prefix); 175 | } 176 | 177 | l += snprintf(buf + l, sizeof(buf) - l, 178 | "[%"PRIu64"] %s", 179 | self->seqno, self->topic); 180 | 181 | if (debug > 2) { 182 | unsigned i; 183 | 184 | l += snprintf(buf + l, sizeof(buf) - l, 185 | "\n %4zu\t", self->size); 186 | for (i = 0; i < self->size && i < 32; i++) { 187 | l += snprintf(buf + l, sizeof(buf) - l, 188 | "%02x", self->data[i]); 189 | } 190 | } 191 | 192 | dbg("%s", buf); 193 | } 194 | 195 | const char *nlmsg_key(const nlmsg_t *self) 196 | { 197 | return self->topic; 198 | } 199 | 200 | uint64_t nlmsg_seqno(const nlmsg_t *self) 201 | { 202 | return self->seqno; 203 | } 204 | 205 | const void *nlmsg_data(const nlmsg_t *self) 206 | { 207 | return self->data; 208 | } 209 | 210 | static zhashx_t *nlmsg_ifindex; 211 | static zlist_t *nlmsg_pending; 212 | 213 | /* 214 | * The interface hash table uses the ifindex as the key (as opposed to 215 | * the more typical string of some form). 216 | */ 217 | static inline void *_ifindex_key(uint32_t ifindex) 218 | { 219 | return (void *)(uintptr_t)ifindex; 220 | } 221 | 222 | static size_t nlmsg_ifindex_hasher(const void *key) 223 | { 224 | return (uintptr_t)key; 225 | } 226 | 227 | static int nlmsg_ifindex_comparator(const void *item1, const void *item2) 228 | { 229 | return (uintptr_t)item1 - (uintptr_t)item2; 230 | } 231 | 232 | static void nlmsg_pending_purge(uint32_t ifindex) 233 | { 234 | nlmsg_t *nmsg; 235 | int count = 0; 236 | 237 | for (nmsg = zlist_first(nlmsg_pending); 238 | nmsg != NULL; 239 | nmsg = zlist_next(nlmsg_pending)) { 240 | if (nmsg->ifindex != ifindex) 241 | continue; 242 | 243 | zlist_remove(nlmsg_pending, nmsg); 244 | nlmsg_free(nmsg); 245 | count++; 246 | } 247 | 248 | if (count != 0) 249 | dbg("%s(%u) count %d", __func__, ifindex, count); 250 | } 251 | 252 | /* 253 | * Unexpected NETLINK message, encapsulate the message and save it for 254 | * later (arrival of the associated NEWLINK message). 255 | */ 256 | void nlmsg_pending_add(const char *topic, const struct nlmsghdr *nlh, 257 | uint32_t ifindex) 258 | { 259 | nlmsg_t *nmsg; 260 | 261 | nmsg = nlmsg_new(topic, 0, nlh, nlh->nlmsg_len); 262 | if (nmsg == NULL) 263 | panic("can't allocate memory for msg"); 264 | 265 | nmsg->ifindex = ifindex; 266 | if (zlist_append(nlmsg_pending, nmsg) < 0) { 267 | err("can't add message to nlmsg_pending"); 268 | nlmsg_free(nmsg); 269 | return; 270 | } 271 | 272 | if (debug) { 273 | char buf[64]; 274 | 275 | snprintf(buf, sizeof(buf), 276 | "%s(%u) %s", __func__, ifindex, 277 | nlmsg_type_name_rtnl(nlh)); 278 | nlmsg_dump(buf, nmsg); 279 | } 280 | } 281 | 282 | /* 283 | * Publish any/all pending messages associated with the indicated 284 | * interface. 285 | */ 286 | void nlmsg_pending_propagate(uint32_t ifindex, uint64_t *seqno) 287 | { 288 | nlmsg_t *nmsg; 289 | 290 | for (nmsg = zlist_first(nlmsg_pending); 291 | nmsg != NULL; 292 | nmsg = zlist_next(nlmsg_pending)) { 293 | if (nmsg->ifindex != ifindex) 294 | continue; 295 | 296 | zlist_remove(nlmsg_pending, nmsg); 297 | nmsg->seqno = ++(*seqno); 298 | nl_propagate_nlmsg(nmsg); 299 | } 300 | } 301 | 302 | bool nlmsg_ifindex_add(uint32_t ifindex, const char *ifname) 303 | { 304 | const void *key = _ifindex_key(ifindex); 305 | 306 | if (zhashx_lookup(nlmsg_ifindex, key) != NULL) 307 | return false; 308 | 309 | dbg("%s(%u, %s) insert, key %p", __func__, ifindex, 310 | ifname == NULL ? "?" : ifname, key); 311 | 312 | if (zhashx_insert(nlmsg_ifindex, key, "active") < 0) 313 | err("can't insert %u into nlmsg_ifindex", ifindex); 314 | 315 | return true; 316 | } 317 | 318 | bool nlmsg_ifindex_lookup(uint32_t ifindex) 319 | { 320 | return zhashx_lookup(nlmsg_ifindex, _ifindex_key(ifindex)) != NULL; 321 | } 322 | 323 | void nlmsg_ifindex_del(uint32_t ifindex) 324 | { 325 | zhashx_delete(nlmsg_ifindex, _ifindex_key(ifindex)); 326 | dbg("%s(%u)", __func__, ifindex); 327 | nlmsg_pending_purge(ifindex); 328 | } 329 | 330 | void nlmsg_setup(void) 331 | { 332 | nlmsg_ifindex = zhashx_new(); 333 | if (nlmsg_ifindex == NULL) 334 | panic("can't create nlmsg_ifindex"); 335 | 336 | /* 337 | * Our key is a simple integer so no need for any duplicator 338 | * or destructor functions (zhashx defaults to string-based 339 | * keys). 340 | */ 341 | zhashx_set_key_comparator(nlmsg_ifindex, nlmsg_ifindex_comparator); 342 | zhashx_set_key_hasher(nlmsg_ifindex, nlmsg_ifindex_hasher); 343 | zhashx_set_key_duplicator(nlmsg_ifindex, NULL); 344 | zhashx_set_key_destructor(nlmsg_ifindex, NULL); 345 | zhashx_set_destructor(nlmsg_ifindex, NULL); 346 | 347 | nlmsg_pending = zlist_new(); 348 | if (nlmsg_pending == NULL) 349 | panic("can't create nlmsg_pending list"); 350 | } 351 | 352 | void nlmsg_cleanup(void) 353 | { 354 | zlist_destroy(&nlmsg_pending); 355 | zhashx_destroy(&nlmsg_ifindex); 356 | } 357 | 358 | /* 359 | * For use by the unit-test module 360 | */ 361 | zhashx_t *nlmsg_ifindex_hash(void) 362 | { 363 | return nlmsg_ifindex; 364 | } 365 | 366 | zlist_t *nlmsg_pending_list(void) 367 | { 368 | return nlmsg_pending; 369 | } 370 | -------------------------------------------------------------------------------- /daemon/nlmsg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 AT&T Intellectual Property. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-only 5 | */ 6 | #if !defined(__nlmsg_h__) 7 | #define __nlmsg_h__ 8 | 9 | typedef struct _nlmsg nlmsg_t; 10 | 11 | int seqno_sendm(zsock_t *socket, uint64_t seqno); 12 | nlmsg_t *nlmsg_new(const char *str, uint64_t seqno, const void *data, 13 | size_t len); 14 | void nlmsg_free(nlmsg_t *nlmsg); 15 | nlmsg_t *nlmsg_recv(const char *topic, zmsg_t *msg); 16 | int nlmsg_send(nlmsg_t *nlmsg, zsock_t *socket); 17 | void nlmsg_dump(const char *prefix, const nlmsg_t *nlmsg); 18 | nlmsg_t *nlmsg_copy(nlmsg_t *nlmsg); 19 | const char *nlmsg_key(const nlmsg_t *nlmsg); 20 | uint64_t nlmsg_seqno(const nlmsg_t *nlmsg); 21 | const void *nlmsg_data(const nlmsg_t *nlmsg); 22 | 23 | void nlmsg_pending_add(const char *topic, const struct nlmsghdr *nlh, 24 | uint32_t ifindex); 25 | void nlmsg_pending_propagate(uint32_t ifindex, uint64_t *seqno); 26 | bool nlmsg_ifindex_add(uint32_t ifindex, const char *ifname); 27 | bool nlmsg_ifindex_lookup(uint32_t ifindex); 28 | void nlmsg_ifindex_del(uint32_t ifindex); 29 | 30 | zhashx_t *nlmsg_ifindex_hash(void); 31 | zlist_t *nlmsg_pending_list(void); 32 | 33 | void nlmsg_setup(void); 34 | void nlmsg_cleanup(void); 35 | #endif 36 | -------------------------------------------------------------------------------- /daemon/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015-2017 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Configuration file (controller.conf) parser for controller. 9 | */ 10 | #if !defined(__parser_h__) 11 | #define __parser_h__ 12 | 13 | #include "ip_addr.h" 14 | 15 | typedef enum { 16 | PARSE_OK = 1, 17 | PARSE_ERR, 18 | PARSE_IGNORED 19 | } parse_result_t; 20 | 21 | #define PARSE_MAX_EP_LEN 128 22 | 23 | /** 24 | * Is the supplied URL an IPC-based endpoint? If so return a pointer to 25 | * the start of the path. 26 | */ 27 | extern const char * 28 | is_url_ipc(const char *url); 29 | 30 | /* 31 | * Return URLs for use with request & publish ZeroMQ sockets. 32 | */ 33 | extern const char * 34 | parser_endpoint_request(void); 35 | 36 | extern const char * 37 | parser_endpoint_publish(void); 38 | 39 | extern const char * 40 | parser_endpoint_request_bound(void); 41 | 42 | extern const char * 43 | parser_endpoint_publish_bound(void); 44 | 45 | extern void 46 | parser_set_endpoint_request_bound(char *url); 47 | 48 | extern void 49 | parser_set_endpoint_publish_bound(char *url); 50 | 51 | 52 | /** 53 | * Get adddress family of the local address 54 | */ 55 | extern sa_family_t 56 | parser_local_af(void); 57 | 58 | /** 59 | * Get the controller local address 60 | */ 61 | const struct ip_addr * 62 | parser_local_addr(void); 63 | /* 64 | * Timeout, in seconds, for vplane connections. The vplane is assumed to 65 | * have died if nothing has been received within this interval. 66 | */ 67 | extern int 68 | parser_controller_timeout(void); 69 | 70 | extern parse_result_t 71 | parse_atoi(uint32_t *i, const char *value); 72 | 73 | /* 74 | * Is ZeroMQ authentication enabled? 75 | */ 76 | extern bool 77 | parser_authentication_enabled(void); 78 | 79 | /* 80 | * Return ZeroMQ authentication certificate filename 81 | */ 82 | extern const char * 83 | parser_authentication_certificate(void); 84 | 85 | /* 86 | * Return path storing remote ZeroMQ certificates 87 | */ 88 | extern const char * 89 | parser_authentication_path(void); 90 | 91 | /* 92 | * Will kernel routes be used? 93 | */ 94 | extern bool 95 | parser_use_kernel_routes(void); 96 | 97 | /* 98 | * Create JSON string describing the config database 99 | */ 100 | int parser_get_json_config(const char *topic, zmsg_t *msg, char **json); 101 | 102 | /* 103 | * Reset (clear) the current controller configuration. Currently only 104 | * used during unit-testing. 105 | */ 106 | extern void 107 | parser_controller_cfg_destroy(void); 108 | 109 | /* 110 | * Parse and save the configuration provided by the given file. Return values: 111 | * 112 | * 0 - Success, no parsing errors encountered 113 | * < 0 - Parser or file access error 114 | */ 115 | extern int 116 | parser_controller_cfg(const char *fname); 117 | 118 | /** 119 | * Is fabric encryption enabled 120 | * Returns true if enabled, false if disabled 121 | */ 122 | bool 123 | parser_fabric_encrypt_enabled(void); 124 | 125 | /** 126 | * Set fabric encryption 127 | * Set true to enable and false to disable 128 | */ 129 | void 130 | parser_set_fabric_encrypt(bool encrypt); 131 | 132 | /* 133 | * Set controller fabric address. 134 | */ 135 | void 136 | parser_set_fabric_address(struct ip_addr *addr); 137 | 138 | /* 139 | * Delete controller fabric address. 140 | */ 141 | void 142 | parser_delete_fabric_address(struct ip_addr *addr); 143 | 144 | /* 145 | * Returns controller fabric address for given address family 146 | */ 147 | const struct ip_addr * 148 | parser_fabric_addr(sa_family_t af); 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /daemon/protobuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * protobuf.c 3 | * 4 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "VPlanedEnvelope.pb-c.h" 16 | #include "protobuf.h" 17 | #include "controller.h" 18 | 19 | /* 20 | * extract_protobuf() overwrites *cmd with the decoded base64 21 | * and vplane stripped binary representation. The length of 22 | * this will be less than the cmd_len of the passed in format. 23 | * But if *cmd is to be used in an un-modified representation 24 | * a copy must be passed in (see bug 44870). 25 | */ 26 | int 27 | extract_protobuf(char **cmd, int *cmd_len) 28 | { 29 | size_t out_len; 30 | size_t msg_len = strlen(*cmd) - strlen(PROTOBUF_TOPIC) - 1; 31 | if ((int)msg_len < 1) 32 | return -1; 33 | 34 | /* first decode from base64 */ 35 | char buf[msg_len]; 36 | base64_decodestate state; 37 | base64_init_decodestate(&state); 38 | out_len = base64_decode_block( 39 | (*cmd) + strlen(PROTOBUF_TOPIC) + 1, 40 | msg_len, 41 | buf, 42 | &state); 43 | if (buf[0] == '\0' || out_len == 0) { 44 | err("failed to decode base64"); 45 | return -1; 46 | } 47 | 48 | /* strip off vplaned envelope first */ 49 | VPlanedEnvelope *vpd_msg; 50 | vpd_msg = vplaned_envelope__unpack(NULL, 51 | out_len, 52 | (const uint8_t *)buf); 53 | if (vpd_msg == NULL) { 54 | err("failed to unpack vplaned envelope"); 55 | return -1; 56 | } 57 | 58 | *cmd_len = vpd_msg->msg.len; 59 | 60 | if (vpd_msg->msg.len > strlen(*cmd)) { 61 | err("error in decoding binary"); 62 | return -1; 63 | } 64 | 65 | memcpy(*cmd, 66 | vpd_msg->msg.data, 67 | vpd_msg->msg.len); 68 | 69 | vplaned_envelope__free_unpacked(vpd_msg, NULL); 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /daemon/protobuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * protobuf.h 3 | * 4 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | #ifndef _PROTOBUF_H_ 9 | #define _PROTOBUF_H_ 10 | 11 | #define PROTOBUF_TOPIC "protobuf" 12 | 13 | int 14 | extract_protobuf(char **cmd, int *cmd_len); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /daemon/team.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROLLER_TEAM_H 2 | #define __CONTROLLER_TEAM_H 3 | 4 | struct team_port_info { 5 | struct nlmsghdr *nlh; 6 | char *topic; 7 | uint32_t port_ifindex; 8 | int changed; 9 | int linkup; 10 | int removed; 11 | uint32_t speed; 12 | uint8_t duplex; 13 | }; 14 | 15 | struct team_option_info { 16 | struct nlmsghdr *nlh; 17 | char *topic; 18 | uint32_t port_ifindex; 19 | uint32_t changed; 20 | union { 21 | uint8_t u8; 22 | uint32_t u32; 23 | char *str; 24 | void *binary; 25 | } data; 26 | char name[16]; 27 | uint32_t removed; 28 | uint32_t array_index; 29 | uint16_t payload_len; 30 | uint8_t type; 31 | }; 32 | 33 | struct team_msg_desc { 34 | zlist_t *infolist; 35 | struct nlmsghdr orighdr; 36 | uint32_t ifindex; 37 | int cmd; 38 | }; 39 | 40 | int process_genetlink_teamcmd(const struct nlmsghdr *nlh, 41 | struct team_msg_desc *desc); 42 | 43 | void team_msg_data_free(struct team_msg_desc *desc); 44 | 45 | int team_query_portlist(struct mnl_socket *s, int family_id, 46 | unsigned long ifindex, unsigned int dumpseq); 47 | int team_query_options(struct mnl_socket *s, int family_id, 48 | unsigned long ifindex, unsigned int dump_seq); 49 | #endif 50 | -------------------------------------------------------------------------------- /daemon/tunnel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017,2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2012,2014-2017 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Netlink operations on TUN/TAP representation of dataplane interface. 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include "controller.h" 32 | 33 | /* Send request and parse response */ 34 | static int mnl_talk(tun_t *nl, struct nlmsghdr *nlh) 35 | { 36 | unsigned portid = mnl_socket_get_portid(nl); 37 | uint32_t seq = time(NULL); 38 | char buf[MNL_SOCKET_BUFFER_SIZE]; 39 | 40 | nlh->nlmsg_flags |= NLM_F_ACK; 41 | nlh->nlmsg_seq = seq; 42 | 43 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 44 | err("mnl_socket_sendto failed: %s", strerror(errno)); 45 | return -1; 46 | } 47 | 48 | int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 49 | if (ret < 0) { 50 | err("mnl_socket_recvfrom failed: %s", strerror(errno)); 51 | return -1; 52 | } 53 | 54 | return mnl_cb_run(buf, ret, seq, portid, NULL, NULL); 55 | } 56 | 57 | /* Initialize netlink socket */ 58 | tun_t *tun_init(void) 59 | { 60 | struct mnl_socket *nl = mnl_socket_open(NETLINK_ROUTE); 61 | if (!nl) 62 | return NULL; 63 | 64 | if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { 65 | mnl_socket_close(nl); 66 | return NULL; 67 | } 68 | return nl; 69 | } 70 | 71 | void tun_destroy(tun_t *tun) 72 | { 73 | mnl_socket_close(tun); 74 | } 75 | 76 | 77 | /* Use netlink OPERSTATE message to bring link up/down 78 | * is documented in kernel Documentation/networking/operstates.txt 79 | */ 80 | int tun_set_linkstate(tun_t *nl, unsigned int ifindex, unsigned int state) 81 | { 82 | char buf[MNL_SOCKET_BUFFER_SIZE]; 83 | 84 | memset(buf, 0, sizeof(buf)); 85 | struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); 86 | nlh->nlmsg_type = RTM_NEWLINK; 87 | nlh->nlmsg_flags = NLM_F_REQUEST; 88 | 89 | struct ifinfomsg *ifi; 90 | ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); 91 | ifi->ifi_family = AF_UNSPEC; 92 | ifi->ifi_index = ifindex; 93 | 94 | mnl_attr_put_u8(nlh, IFLA_OPERSTATE, state); 95 | 96 | return mnl_talk(nl, nlh); 97 | } 98 | 99 | /* Delete tunnel device attributes */ 100 | int tun_delete(tun_t *nl, unsigned int ifindex) 101 | { 102 | char buf[MNL_SOCKET_BUFFER_SIZE]; 103 | 104 | dbg("tunnel %u delete", ifindex); 105 | 106 | struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); 107 | nlh->nlmsg_type = RTM_DELLINK; 108 | nlh->nlmsg_flags = NLM_F_REQUEST; 109 | struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); 110 | 111 | ifi->ifi_family = AF_UNSPEC; 112 | ifi->ifi_index = ifindex; 113 | 114 | return mnl_talk(nl, nlh); 115 | } 116 | 117 | static int tun_set_flags(tun_t *nl, unsigned int ifindex, 118 | unsigned long flags, unsigned long changed) 119 | { 120 | char buf[MNL_SOCKET_BUFFER_SIZE]; 121 | 122 | struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); 123 | nlh->nlmsg_type = RTM_NEWLINK; 124 | nlh->nlmsg_flags = NLM_F_REQUEST; 125 | 126 | struct ifinfomsg *ifi; 127 | ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); 128 | ifi->ifi_index = ifindex; 129 | ifi->ifi_family = AF_UNSPEC; 130 | ifi->ifi_change = changed; 131 | ifi->ifi_flags = flags; 132 | 133 | return mnl_talk(nl, nlh); 134 | } 135 | 136 | /* Callback (from mnl_cb_run) to get flags */ 137 | static int process_nl_get_flags(const struct nlmsghdr *nlh, void *arg) 138 | { 139 | const struct ifinfomsg *ifi = mnl_nlmsg_get_payload(nlh); 140 | unsigned long *flags = arg; 141 | 142 | *flags = ifi->ifi_flags; 143 | return MNL_CB_OK; 144 | } 145 | 146 | /* Get tunnel flags */ 147 | static int tun_get_flags(tun_t *nl, unsigned int ifindex, 148 | unsigned long *flags) 149 | { 150 | char buf[MNL_SOCKET_BUFFER_SIZE]; 151 | uint32_t seq = time(NULL); 152 | unsigned portid = mnl_socket_get_portid(nl); 153 | 154 | struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); 155 | 156 | nlh->nlmsg_type = RTM_GETLINK; 157 | nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; 158 | nlh->nlmsg_seq = seq; 159 | 160 | struct ifinfomsg *ifi; 161 | 162 | ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); 163 | ifi->ifi_index = ifindex; 164 | ifi->ifi_family = AF_UNSPEC; 165 | 166 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 167 | err("get flags(%u)mnl_socket_sendto failed: %s", ifindex, 168 | strerror(errno)); 169 | return -1; 170 | } 171 | ssize_t len; 172 | int ret; 173 | 174 | while ((len = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) { 175 | ret = mnl_cb_run(buf, len, seq, portid, 176 | process_nl_get_flags, flags); 177 | if (ret < MNL_CB_STOP) { 178 | err("get flags(%u) error: %s", ifindex, 179 | strerror(errno)); 180 | return -1; 181 | } 182 | if (ret == MNL_CB_STOP) 183 | break; 184 | } 185 | if (len < 0) { 186 | err("get flags(%u) recv error: %s", ifindex, 187 | strerror(errno)); 188 | return -1; 189 | } 190 | return 0; 191 | } 192 | 193 | /* Check if interface is admin up */ 194 | bool tun_admin_is_up(tun_t *self, unsigned int ifindex) 195 | { 196 | unsigned long flags = 0; 197 | 198 | if (tun_get_flags(self, ifindex, &flags) < 0) { 199 | err("tunnel %u unable to get admin state", ifindex); 200 | return false; 201 | } 202 | dbg("tunnel %u is admin %s", ifindex, (flags & IFF_UP)?"UP":"DOWN"); 203 | if (flags & IFF_UP) 204 | return true; 205 | 206 | return false; 207 | } 208 | 209 | /* Toggle admin status of tunnel */ 210 | void tun_admin_toggle(tun_t *self, unsigned int ifindex) 211 | { 212 | if (tun_set_flags(self, ifindex, ~IFF_UP, IFF_UP) < 0) { 213 | dbg("tunnel %u toggle-admin DOWN failed %s", ifindex, 214 | strerror(errno)); 215 | return; 216 | } 217 | if (tun_set_flags(self, ifindex, IFF_UP, IFF_UP) < 0) { 218 | dbg("tunnel %u toggle-admin UP failed %s", ifindex, 219 | strerror(errno)); 220 | } 221 | dbg("tunnel %u admin state toggled", ifindex); 222 | } 223 | 224 | /* Put the tunnel in dormant mode */ 225 | int tun_set_dormant(tun_t *nl, const char *ifname) 226 | { 227 | char buf[MNL_SOCKET_BUFFER_SIZE]; 228 | 229 | struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); 230 | nlh->nlmsg_type = RTM_NEWLINK; 231 | nlh->nlmsg_flags = NLM_F_REQUEST; 232 | 233 | struct ifinfomsg *ifi; 234 | ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); 235 | ifi->ifi_family = AF_UNSPEC; 236 | ifi->ifi_flags = 0; 237 | 238 | mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname); 239 | 240 | mnl_attr_put_u8(nlh, IFLA_LINKMODE, IF_LINK_MODE_DORMANT); 241 | /* Default to no carrier, link up happens later */ 242 | mnl_attr_put_u8(nlh, IFLA_OPERSTATE, IF_OPER_DORMANT); 243 | 244 | return mnl_talk(nl, nlh); 245 | } 246 | -------------------------------------------------------------------------------- /daemon/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-only 5 | * Copyright (c) 2012-2016 by Brocade Communications Systems, Inc. 6 | * 7 | * Genericish wrapper around logging. 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | #include "controller.h" 28 | 29 | int debug; 30 | int udp_fd; 31 | 32 | void __panic(const char *funcname, const char *format, ...) 33 | { 34 | va_list ap; 35 | const char *err = errno ? strerror(errno) : NULL; 36 | char line[1024]; 37 | 38 | va_start(ap, format); 39 | vsnprintf(line, sizeof(line), format, ap); 40 | va_end(ap); 41 | 42 | fprintf(stderr, "%s(): %s\n", funcname, line); 43 | 44 | syslog(LOG_CRIT, "PANIC in %s():\n", funcname); 45 | if (err) 46 | syslog(LOG_CRIT, "%s: %s", line, err); 47 | else 48 | syslog(LOG_CRIT, "%s", line); 49 | 50 | abort(); 51 | } 52 | 53 | /* Startup problem */ 54 | void die(const char *format, ...) 55 | { 56 | char line[1024]; 57 | va_list ap; 58 | 59 | va_start(ap, format); 60 | vsnprintf(line, sizeof(line), format, ap); 61 | va_end(ap); 62 | syslog(LOG_ERR, "fatal: %s", line); 63 | exit(EXIT_FAILURE); 64 | } 65 | 66 | /* Protocol or other error */ 67 | void logit(int level, char c, const char *format, ...) 68 | { 69 | char line[1024]; 70 | va_list ap; 71 | 72 | va_start(ap, format); 73 | vsnprintf(line, sizeof(line), format, ap); 74 | va_end(ap); 75 | 76 | if (debug) 77 | zclock_log("%c: %s", c, line); 78 | else 79 | syslog(level, "%s", line); 80 | } 81 | 82 | #ifdef DEBUG 83 | void dump_netlink(const struct nlmsghdr *nlh) 84 | { 85 | /* extra header based on type of message */ 86 | static const size_t rtm_header_size[RTM_NR_FAMILIES] = { 87 | [RTM_FAM(RTM_NEWLINK)] = sizeof(struct ifinfomsg), 88 | [RTM_FAM(RTM_NEWADDR)] = sizeof(struct ifaddrmsg), 89 | [RTM_FAM(RTM_NEWROUTE)] = sizeof(struct rtmsg), 90 | [RTM_FAM(RTM_NEWRULE)] = sizeof(struct fib_rule_hdr), 91 | [RTM_FAM(RTM_NEWQDISC)] = sizeof(struct tcmsg), 92 | [RTM_FAM(RTM_NEWTCLASS)] = sizeof(struct tcmsg), 93 | [RTM_FAM(RTM_NEWTFILTER)] = sizeof(struct tcmsg), 94 | [RTM_FAM(RTM_NEWACTION)] = sizeof(struct tcamsg), 95 | [RTM_FAM(RTM_GETMULTICAST)] = sizeof(struct rtgenmsg), 96 | [RTM_FAM(RTM_GETANYCAST)] = sizeof(struct rtgenmsg), 97 | [RTM_FAM(RTM_NEWNETCONF)] = sizeof(struct netconfmsg), 98 | }; 99 | 100 | mnl_nlmsg_fprintf(stderr, nlh, nlh->nlmsg_len, 101 | rtm_header_size[RTM_FAM(nlh->nlmsg_type)]); 102 | } 103 | #endif 104 | -------------------------------------------------------------------------------- /daemon/vplane.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2020, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015-2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Controller vplane (dataplane) database and associated access API's. 9 | */ 10 | #if !defined(__vplane_h__) 11 | #define __vplane_h__ 12 | 13 | #include 14 | #include "parser.h" 15 | 16 | typedef struct vplane_ vplane_t; 17 | 18 | /* Do we have any remote vplanes configured? */ 19 | bool vplane_remote(void); 20 | 21 | /* Local vplane (co-located with controller)? */ 22 | bool vplane_is_local(const vplane_t *vp); 23 | 24 | /* Is the vplane connected (and by implication authenticated)? */ 25 | bool vplane_is_connected(const vplane_t *vp); 26 | 27 | /* Has the vplane configuration changed? */ 28 | bool vplane_is_update_needed(const vplane_t *vp); 29 | 30 | int vplane_get_id(const vplane_t *vp); 31 | const char *vplane_get_uuid(const vplane_t *vp); 32 | const char *vplane_get_control(const vplane_t *vp); 33 | zlist_t *vplane_get_upaddr_list(const vplane_t *vp); 34 | zframe_t *vplane_get_envelope(const vplane_t *vp); 35 | 36 | /* 37 | * Set (or update) the ZeroMQ URL used to retrieve operational data from 38 | * the dataplane. 39 | * 40 | * 0 - success 41 | * <0 - error 42 | */ 43 | int vplane_set_control(vplane_t *vp, const char *url); 44 | 45 | /* 46 | * Locate a vplane instance using various "keys": 47 | * 48 | * o session - use the 0MQ message envelope to locate a connected vplane 49 | * 50 | * o UUID - use the UUID to locate a configured vplane 51 | * 52 | * o IPv4 - use the IPv4 control address to locate a configured vplane 53 | * 54 | * The latter is for backwards compatibility 55 | */ 56 | vplane_t *vplane_findbysession(zframe_t *envelope); 57 | vplane_t *vplane_findbyuuid(const char *uuid); 58 | const vplane_t *vplane_findbyid(int id); 59 | 60 | /* Vplane walker function prototype */ 61 | typedef void vplane_iter_func_t(const vplane_t *vp, void *arg); 62 | 63 | /* Walker function for all vplanes */ 64 | void vplane_walk_all(vplane_iter_func_t func, void *arg); 65 | 66 | /* 67 | * Obtain the name and index associated with an interface on a specific 68 | * vplane instance. 69 | */ 70 | const char *vplane_iface_get_ifname(const vplane_t *vp, uint32_t ifn); 71 | uint32_t vplane_iface_get_ifindex(const vplane_t *vp, uint32_t ifn); 72 | 73 | /* Storage for a file descriptor if the interface is remote */ 74 | int vplane_iface_get_fd(const vplane_t *vp, uint32_t ifn); 75 | int vplane_iface_set_fd(const vplane_t *vp, uint32_t ifn, int fd); 76 | 77 | /* Update the state of an interface on a specific vplane instance. */ 78 | int vplane_iface_set_state(const vplane_t *vp, uint32_t ifn, 79 | uint32_t operstate); 80 | 81 | /* Delete an interface from a specific vplane instance */ 82 | void vplane_iface_del(vplane_t *vp, uint32_t ifn); 83 | 84 | /* Add an interface to a specific vplane instance. */ 85 | int vplane_iface_add(vplane_t *vp, uint32_t ifn, uint32_t ifindex, 86 | const char *ifname); 87 | 88 | void *vplane_iface_get_cookie(const vplane_t *vp, uint32_t ifn); 89 | int vplane_iface_set_cookie(const vplane_t *vp, uint32_t ifn, void *cookie); 90 | 91 | bool vplane_iface_get_delpend(const vplane_t *vp, uint32_t ifn); 92 | int vplane_iface_set_delpend(const vplane_t *vp, uint32_t ifn, bool delpend); 93 | 94 | /* 95 | * Function used to iterate (walk) all defined interfaces for a 96 | * specific vplane instance 97 | */ 98 | typedef void vplane_iface_iter_func_t(const vplane_t *vp, uint32_t ifn, 99 | void *arg); 100 | void vplane_iface_iterate(const vplane_t *vp, vplane_iface_iter_func_t func, 101 | void *arg); 102 | 103 | /* 104 | * Using the message envelope as the session key, mark a vplane as 105 | * connected. 106 | */ 107 | int vplane_local_connect(vplane_t *vp, zframe_t *envelope); 108 | 109 | int vplane_connect(vplane_t *vp, zframe_t *envelope); 110 | 111 | /* Clock tick - update (expire) the timer values for each vplane */ 112 | void vplane_tick(void); 113 | 114 | /* Update the keepalive timer for the indicated vplane. */ 115 | void vplane_keepalive(vplane_t *vp, const char *request, uint32_t ifno); 116 | 117 | /* Parse an individual vplane configuration attribute - name/value pair. */ 118 | parse_result_t vplane_cfg_set_attribute(int id, const char *name, 119 | const char *value); 120 | 121 | /* Configuration mode */ 122 | void vplane_cfg_failed(void); 123 | void vplane_cfg_end(void); 124 | void vplane_cfg_begin(void); 125 | 126 | /* 127 | * Create JSON string describing the vplane database 128 | */ 129 | int vplane_get_json_config(const char *topic, zmsg_t *msg, char **json); 130 | 131 | /* Initialize the vplane module */ 132 | void vplane_setup(void); 133 | 134 | /* Forcibly disconnect all vplane instances */ 135 | void vplane_disconnect_all(void); 136 | 137 | /* 138 | * Cleanup the vplane module. 139 | */ 140 | void vplane_teardown(void); 141 | 142 | /* 143 | * Test-only function used to set the vplane timeout in milliseconds in 144 | * order to keep the test times down to a reasonable level - 10ms vs 145 | * 10s. 146 | */ 147 | void vplane_test_set_timeout(int id, int ms); 148 | #endif 149 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | vplane-controller (3.7.1) unstable; urgency=medium 2 | 3 | [ Neil McGill ] 4 | * vplaned: ignore unresolved mroute netlink updates (Fixes: VRVDR-47352) 5 | 6 | [ Mark Gillott ] 7 | * vplaned: fixup indentation and formatting 8 | 9 | -- Mark Gillott Wed, 29 Sep 2021 11:23:13 +0100 10 | 11 | vplane-controller (3.7.0) unstable; urgency=medium 12 | 13 | * json-c updated following move to debian bullsye (Fixes: VRVDR-54768) 14 | 15 | -- Mark Gillott Tue, 23 Mar 2021 09:56:55 +0000 16 | 17 | vplane-controller (3.6.14) unstable; urgency=medium 18 | 19 | [ Shweta Choudaha ] 20 | * request:Add lock for port creation 21 | 22 | -- Mark Gillott Tue, 10 Nov 2020 07:41:33 +0000 23 | 24 | vplane-controller (3.6.13) unstable; urgency=medium 25 | 26 | [ Sharmila Podury ] 27 | * snmp: JSON parse error on dataplane response 28 | 29 | -- Mark Gillott Tue, 20 Oct 2020 11:17:03 +0100 30 | 31 | vplane-controller (3.6.12) unstable; urgency=medium 32 | 33 | [ Charles (Chas) Williams ] 34 | * snapshot: decode protobufs (Bugfix: VRVDR-47789) 35 | 36 | -- Mark Gillott Thu, 06 Aug 2020 08:00:40 +0100 37 | 38 | vplane-controller (3.6.11) unstable; urgency=medium 39 | 40 | [ Charles (Chas) Williams ] 41 | * snapshot: decode binary messages as escaped hex 42 | 43 | -- Mark Gillott Mon, 03 Aug 2020 18:27:44 +0100 44 | 45 | vplane-controller (3.6.10) unstable; urgency=medium 46 | 47 | [ Nicholas Brown ] 48 | * Add optional support for building vplaned with jemalloc 49 | 50 | -- Mark Gillott Fri, 17 Jul 2020 10:08:36 +0100 51 | 52 | vplane-controller (3.6.9) unstable; urgency=medium 53 | 54 | [ Nicholas Brown ] 55 | * Port to meson to better manage complex build dependencies and installation 56 | * Add copyright/license header to all meson.build files 57 | 58 | -- Mark Gillott Tue, 07 Jul 2020 11:45:52 +0100 59 | 60 | vplane-controller (3.6.8) unstable; urgency=medium 61 | 62 | [ Robert Shearman ] 63 | * port: delete kernel interfaces for local vplanes 64 | * request: actually delete the port when purging an interface 65 | * request: don't require ifindex for DELPORT message 66 | 67 | -- Mark Gillott Wed, 01 Jul 2020 13:24:15 +0100 68 | 69 | vplane-controller (3.6.7) unstable; urgency=medium 70 | 71 | [ Nicholas Brown ] 72 | * fix syntax-error-in-dep5-copyright lintian error 73 | * git ignore additional package build directories 74 | * fix ancient-standards-version lintian error 75 | * Add missing dependencies to libvplaned-dev 76 | * add missing dev dependency 77 | * Correct dependencies on perl/python for protobuf libraries 78 | * Only install header in the top level dir 79 | 80 | -- Mark Gillott Fri, 26 Jun 2020 13:23:30 +0100 81 | 82 | vplane-controller (3.6.6) unstable; urgency=medium 83 | 84 | [ Paul Atkins ] 85 | * cstore: add an internal version of vplaned_cstore_request 86 | * cstore: modify vplaned_cstore_request_internal to support pbs 87 | * cstore: add an api to store config in a protobuf message 88 | * lib: change libvplaned to be a shared library 89 | 90 | -- Mark Gillott Mon, 22 Jun 2020 16:50:31 +0100 91 | 92 | vplane-controller (3.6.5) unstable; urgency=medium 93 | 94 | [ Robert Shearman ] 95 | * port: set ethtool speed/duplex values to unknown on creating a port 96 | (Fixes: VRVDR-45369) 97 | 98 | [ Nicholas Brown ] 99 | * Install package changelogs 100 | * Don't log to a file, let systemd redirect to journal 101 | 102 | -- Mark Gillott Mon, 15 Jun 2020 11:30:54 +0100 103 | 104 | vplane-controller (3.6.4) unstable; urgency=medium 105 | 106 | [ Michael Doyle ] 107 | * Update License 108 | 109 | [ Mark Gillott ] 110 | * ports: replace NEWPORT with INIPORT & ADDPORT (Fixes: VRVDR-46511) 111 | * netlink: ensure interface exists before publishing other messages 112 | (Fixes: VRVDR-46511) 113 | * ut: add unit-test module for netlink message handling (Fixes: VRVDR-46511) 114 | * netlink: don't bother to explicitly collect netconf 115 | * snapshot: clear dead ports from snapshot (Fixes: VRVDR-46511) 116 | 117 | -- Mark Gillott Fri, 29 May 2020 14:39:12 +0100 118 | 119 | vplane-controller (3.6.3) unstable; urgency=medium 120 | 121 | * Don't sort cstore items that are bound to an interface (Fixes: VRVDR-50606) 122 | 123 | -- Mark Gillott Thu, 23 Apr 2020 09:56:00 +0100 124 | 125 | vplane-controller (3.6.2) unstable; urgency=medium 126 | 127 | [ Nicholas Brown ] 128 | * Remove copyright and license assertion output 129 | 130 | -- Mark Gillott Tue, 14 Apr 2020 10:12:12 +0100 131 | 132 | vplane-controller (3.6.1) unstable; urgency=medium 133 | 134 | [ John Southworth ] 135 | * Add Go programming language bindings for the controller protobufs 136 | * Add C++ language bindings for controller protobufs 137 | 138 | [ Robert Shearman ] 139 | * daemon: ensure newlink for bridge member is removed from snapshot on delete 140 | (Fixes: VRVDR-49601) 141 | * snapshot: add separate levels for team and bridge slaves 142 | (Fixes: VRVDR-49763) 143 | 144 | -- Mark Gillott Wed, 29 Jan 2020 11:18:59 +0000 145 | 146 | vplane-controller (3.5.16) unstable; urgency=medium 147 | 148 | * DANOS Import master 149 | 150 | -- Mark Gillott Wed, 06 Nov 2019 15:04:02 +0000 151 | 152 | vplane-controller (3.5.10.danos2) unstable; urgency=medium 153 | 154 | * DANOS Import 155 | 156 | -- Mark Gillott Wed, 06 Nov 2019 11:08:51 +0000 157 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: vplane-controller 2 | Section: net 3 | Priority: optional 4 | Maintainer: Vyatta Package Maintainers 5 | Build-Depends: debhelper (>= 9), 6 | dh-exec, 7 | libinih-dev, 8 | libmnl-dev, 9 | libzmq3-dev (>= 4.0), 10 | libczmq-dev, 11 | libedit-dev, 12 | libjson-c-dev (>= 0.10), 13 | libvrfmanager-vyatta-dev (>= 2.4), 14 | cpputest (>= 3.8), 15 | pkg-config, 16 | meson, 17 | debhelper (>= 9.20160709) | dh-systemd, 18 | bvnos-linux-libc-dev, 19 | libb64-dev, 20 | protobuf-c-compiler, 21 | protobuf-compiler, 22 | libprotobuf-c-dev, 23 | perl (>= 5.8.8), 24 | libgoogle-protocolbuffers-perl, 25 | libsystemd-dev, 26 | golang-goprotobuf-dev, 27 | libprotobuf-dev, 28 | libvyatta-dataplane-proto-support, 29 | libjemalloc-dev 30 | Standards-Version: 4.3.0 31 | 32 | Package: libvplaned-dev 33 | Architecture: any 34 | Priority: optional 35 | Section: libdevel 36 | Depends: ${misc:Depends}, 37 | libvplaned1 (= ${binary:Version}), 38 | libczmq-dev, 39 | libjson-c-dev, 40 | libprotobuf-c-dev, 41 | libb64-dev 42 | Description: Vyatta development package for the controller 43 | Development header and library files for the Vyatta controller access 44 | library. 45 | 46 | Package: libvplaned1 47 | Architecture: any 48 | Depends: ${shlibs:Depends}, ${misc:Depends} 49 | Description: Vyatta library package for the controller 50 | Vyatta controller access library. 51 | 52 | Package: vplane-controller 53 | Architecture: any 54 | Replaces: vyatta-controller 55 | Conflicts: vyatta-controller 56 | Depends: adduser, 57 | ${perl:Depends}, ${shlibs:Depends}, ${misc:Depends}, lsb-base (>= 3.0-6) 58 | Breaks: vyatta-routing (<= 1:10.11.9), vyatta-dataplane (<= 3.5.42) 59 | Description: Vyatta dataplane controller 60 | Daemon for controlling Vyatta dataplane services. 61 | 62 | Package: vplane-controller-dbg 63 | Architecture: any 64 | Section: debug 65 | Depends: 66 | vplane-controller (= ${binary:Version}), 67 | ${misc:Depends} 68 | Description: Debugging symbols for vplane-controller 69 | Debugging symbols for the vplane-controller package. 70 | 71 | Package: libvyatta-controller-proto-support 72 | Section: devel 73 | Architecture: all 74 | Depends: ${misc:Depends}, ${perl:Depends}, python3, 75 | libgoogle-protocolbuffers-perl, 76 | python3-protobuf 77 | Description: Vyatta controller protocol buffer files for language support 78 | Perl and Python files to build protobuf messages to talk to the controller 79 | 80 | Package: golang-github-danos-vyatta-controller-protobuf-dev 81 | Architecture: all 82 | Depends: golang-goprotobuf-dev, ${misc:Depends} 83 | Description: Provides Go language bindings for the controller API. 84 | Go bindings for the controller API. 85 | 86 | Package: libvyatta-controller-proto1 87 | Section: devel 88 | Architecture: any 89 | Depends: ${shlibs:Depends}, ${misc:Depends} 90 | Description: Vyatta controller protocol buffer file for language support 91 | C++ bindings for the dataplane API. 92 | 93 | Package: libvyatta-controller-proto-dev 94 | Section: libdevel 95 | Architecture: any 96 | Depends: libvyatta-controller-proto1 (= ${binary:Version}), ${misc:Depends} 97 | Description: Provides C++ headers for the dataplane API. 98 | C++ headers for the dataplane API. 99 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | 3 | Files: * 4 | Copyright: 2018-2021 AT&T Intellectual Property. All rights reserved. 5 | 2012-2017 Brocade Communications Systems, Inc. 6 | License: LGPL-2.1 7 | 8 | Files: 9 | daemon/bstr.c 10 | daemon/bstr.h 11 | Copyright: 12 | Copyright 2010 Derek Fawcus. 13 | License: BSD-3-Clause 14 | 15 | License: LGPL-2.1 16 | This library is free software; you can redistribute it and/or modify 17 | it under the terms of the GNU Lesser General Public License as 18 | published by the Free Software Foundation; version 2.1. 19 | . 20 | This library is distributed in the hope that it will be useful, but 21 | WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 | Lesser General Public License for more details. 24 | . 25 | You should have received a copy of the GNU Lesser General Public 26 | License along with this library; if not, write to the Free Software 27 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 28 | 02110-1301 USA 29 | . 30 | On Debian systems, the full text of the GNU Lesser General Public 31 | License version 2.1 can be found in the file 32 | "/usr/share/common-licenses/LGPL-2.1". 33 | 34 | 35 | License: BSD-3-Clause 36 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 37 | . 38 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 39 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 40 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 41 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 | -------------------------------------------------------------------------------- /debian/golang-github-danos-vyatta-controller-protobuf-dev.install: -------------------------------------------------------------------------------- 1 | usr/share/gocode/src 2 | -------------------------------------------------------------------------------- /debian/libvplaned-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/*.h 2 | usr/lib/*/libvplaned.so 3 | usr/lib/*/pkgconfig/libvplaned.pc 4 | -------------------------------------------------------------------------------- /debian/libvplaned1.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/libvplaned.so.* -------------------------------------------------------------------------------- /debian/libvyatta-controller-proto-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/vyatta-controller/proto/VPlanedEnvelope.pb.h 2 | usr/lib/*/pkgconfig/vyatta-controller-proto.pc 3 | usr/lib/*/libvyatta-controller-proto.so 4 | -------------------------------------------------------------------------------- /debian/libvyatta-controller-proto-support.install: -------------------------------------------------------------------------------- 1 | usr/share/vyatta-dataplane/protobuf/VPlanedEnvelope.proto 2 | usr/share/perl5/vyatta/proto/VPlanedEnvelope.pm 3 | usr/lib/python3/dist-packages/vyatta/proto/VPlanedEnvelope_pb2.py 4 | -------------------------------------------------------------------------------- /debian/libvyatta-controller-proto1.install: -------------------------------------------------------------------------------- 1 | /usr/lib/*/libvyatta-controller-proto.so.* 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #! /usr/bin/make -f 2 | 3 | # Use hardening options 4 | # 5 | # This breaks the unit-test cases, but there is a workaround over in the 6 | # UT makefile. 7 | # 8 | export DEB_BUILD_HARDENING=1 9 | 10 | # Uncomment this to turn on verbose mode. 11 | #export DH_VERBOSE=1 12 | 13 | ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) 14 | export DEB_CFLAGS_MAINT_STRIP=-O2 15 | export DEB_CXXFLAGS_MAINT_STRIP=-O2 16 | export DEB_CFLAGS_MAINT_APPEND=-O3 17 | export DEB_CXXFLAGS_MAINT_APPEND=-O3 18 | endif 19 | 20 | ifneq (,$(filter pkg.vyatta-controller.jemalloc,$(DEB_BUILD_PROFILES))) 21 | USE_JEMALLOC="-Duse_jemalloc=enabled" 22 | endif 23 | 24 | %: 25 | dh $@ --parallel --with systemd 26 | 27 | override_dh_auto_configure: 28 | dh_auto_configure -- $(USE_JEMALLOC) 29 | 30 | override_dh_strip: 31 | dh_strip --dbg-package=vplane-controller-dbg 32 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/vplane-controller.init.d: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: vplane-controller 5 | # Required-Start: $syslog $remote_fs $local_fs 6 | # Required-Stop: $syslog $remote_fs $local_fs 7 | # X-Start-Before: vyatta-router vyatta-routing 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: dataplane controller 11 | # Description: vplane controller system setup 12 | ### END INIT INFO 13 | # 14 | # Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 15 | # Copyright (c) 2015-2016 by Brocade Communications Systems, Inc. 16 | # All rights reserved. 17 | # 18 | # SPDX-License-Identifier: LGPL-2.1-only 19 | # 20 | 21 | DESC="vPlane controller" 22 | NAME=vplaned 23 | PIDFILE=/var/run/vyatta/vplaned.pid 24 | CONFILE=/etc/vyatta/controller.conf 25 | LOGFILE=/var/log/vyatta/$NAME.log 26 | 27 | source /etc/default/vyatta 28 | 29 | : "${vyatta_prefix:=/opt/vyatta}" 30 | : "${vyatta_sbindir:=${vyatta_prefix}/sbin}" 31 | [[ $PATH == *${vyatta_sbindir}* ]] || PATH+=:${vyatta_sbindir} 32 | export PATH 33 | DAEMON=${vyatta_sbindir}/vplaned 34 | 35 | . /lib/lsb/init-functions 36 | 37 | [ -d /var/run/vyatta ] || mkdir -p /var/run/vyatta 38 | 39 | case "$1" in 40 | start) 41 | if [ ! -r $CONFILE ]; then 42 | echo "Missing $CONFILE" 43 | exit 0 44 | fi 45 | 46 | log_action_begin_msg "Starting $DESC" 47 | 48 | /lib/vplane/hwbinding /config/config.boot 49 | start-stop-daemon --start --quiet --pidfile $PIDFILE \ 50 | --exec "$DAEMON" -- "$DEBUG" --daemon -p $PIDFILE -u vplaned -g adm \ 51 | -l $LOGFILE 52 | log_action_end_msg $? 53 | ;; 54 | 55 | stop) 56 | 57 | log_action_begin_msg "Stopping $DESC" 58 | start-stop-daemon --stop --quiet --retry=TERM/5/KILL/5 \ 59 | --pidfile=$PIDFILE \ 60 | --oknodo --exec "$DAEMON" 61 | rm -f $PIDFILE 62 | log_action_end_msg $? 63 | ;; 64 | 65 | status) 66 | status_of_proc -p $PIDFILE "$DAEMON" "$DESC" 67 | ;; 68 | 69 | reload) 70 | log_action_begin_msg "Reloading $DESC config" 71 | start-stop-daemon --stop --signal HUP --quiet --pidfile=$PIDFILE \ 72 | --exec "$DAEMON" 73 | log_action_end_msg $? 74 | ;; 75 | 76 | force-reload|restart) 77 | $0 stop && $0 start 78 | ;; 79 | 80 | *) log_failure_msg "action unknown: $1" ; 81 | false ;; 82 | esac 83 | exit $? 84 | -------------------------------------------------------------------------------- /debian/vplane-controller.install: -------------------------------------------------------------------------------- 1 | opt/vyatta/bin/ 2 | opt/vyatta/sbin/ 3 | tools/* lib/vplane 4 | -------------------------------------------------------------------------------- /debian/vplane-controller.lintian-overrides: -------------------------------------------------------------------------------- 1 | vplane-controller: dir-or-file-in-opt 2 | -------------------------------------------------------------------------------- /debian/vplane-controller.preinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # preinst script for vplane-controller 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `install' 10 | # * `install' 11 | # * `upgrade' 12 | # * `abort-upgrade' 13 | # for details, see http://www.debian.org/doc/debian-policy/ or 14 | # the debian-policy package 15 | 16 | case "$1" in 17 | install|upgrade) 18 | # creating vyattacfg group if it isn't already there 19 | if ! getent group vyattacfg >/dev/null; then 20 | addgroup --system vyattacfg >/dev/null 21 | fi 22 | 23 | # create user vplaned if not already there 24 | if ! getent passwd vplaned >/dev/null; then 25 | adduser --quiet --system \ 26 | --ingroup vyattacfg \ 27 | --home /var/run/vplaned \ 28 | --gecos "Vyatta Dataplane Controller" \ 29 | --shell /bin/false \ 30 | vplaned >/dev/null 31 | fi 32 | ;; 33 | 34 | abort-upgrade) 35 | ;; 36 | 37 | *) 38 | echo "preinst called with unknown argument \`$1'" >&2 39 | exit 1 40 | ;; 41 | esac 42 | 43 | # dh_installdeb will replace this with shell code automatically 44 | # generated by other debhelper scripts. 45 | 46 | #DEBHELPER# 47 | 48 | exit 0 49 | -------------------------------------------------------------------------------- /debian/vplane-controller.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=dataplane controller 3 | Wants=system-preconfigure.service system-configure.service 4 | After=system-preconfigure.service 5 | Before=system-configure.service vyatta-routing.service nsm.service \ 6 | bgpd.service imi.service mribd.service msdpd.service oamd.service \ 7 | ospf6d.service ospfd.service pimd.service ribd.service ripd.service \ 8 | ripngd.service 9 | ConditionPathExists=/etc/vyatta/controller.conf 10 | 11 | [Service] 12 | Type=notify 13 | ExecStartPre=-/sbin/modprobe team 14 | ExecStartPre=-/sbin/modprobe l2tp_netlink 15 | ExecStartPre=/lib/vplane/hwbinding /config/config.boot 16 | ExecStartPre=/bin/bash -c "rm -f /var/run/vyatta/*.stats" 17 | Environment=VPLANED_DEBUG_FLAG="" 18 | ExecStart=/opt/vyatta/sbin/vplaned ${VPLANED_DEBUG_FLAG} -u vplaned -g adm 19 | ExecReload=/bin/kill -HUP $MAINPID 20 | Restart=on-failure 21 | TimeoutStartSec=90s 22 | 23 | [Install] 24 | WantedBy=config-loaded.target 25 | -------------------------------------------------------------------------------- /debian/vplane-controller.socket: -------------------------------------------------------------------------------- 1 | [Socket] 2 | ListenStream=/var/run/vyatta/vplaned-config.socket 3 | 4 | [Install] 5 | WantedBy=sockets.target 6 | -------------------------------------------------------------------------------- /examples/controller.conf: -------------------------------------------------------------------------------- 1 | # Dataplane controller configuration (generated) 2 | [Controller] 3 | publish=5903 4 | request=5904 5 | 6 | [Dataplane.fabric0] 7 | uuid=0f8dfb6a-b5af-11e1-aa94-0023549108c1 8 | address=10.0.0.1 9 | control=5907 10 | 11 | [Dataplane.fabric0] 12 | uuid=073e640e-e8b8-11e1-9c92-0023549108c1 13 | address=10.0.0.2 14 | control=5907 15 | -------------------------------------------------------------------------------- /examples/dataplane.conf: -------------------------------------------------------------------------------- 1 | # Controller on same machine 2 | [Controller] 3 | publish=5568 4 | request=5569 5 | ip=127.0.0.1 6 | 7 | [Dataplane] 8 | uuid=03b9b14e-6311-11e1-9946-0023549108c2 9 | ip=127.0.0.1 10 | control=5907 11 | blacklist=eth4 12 | -------------------------------------------------------------------------------- /lib/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | vplaned_library_sources = files( 5 | 'vplaned_cfg.c', 6 | 'vplaned_cstore.c', 7 | 'vplaned_event.c' 8 | ) 9 | 10 | dataplane_protobuf_c = custom_target('dataplane_protobuf_c', 11 | input: '/usr/share/vyatta-dataplane/protobuf/DataplaneEnvelope.proto', 12 | output: ['DataplaneEnvelope.pb-c.c', 'DataplaneEnvelope.pb-c.h'], 13 | command: [protoc, '--proto_path=/usr/share/vyatta-dataplane/protobuf/', '--c_out=@OUTDIR@', '@INPUT@'], 14 | ) 15 | 16 | vplaned_library = library( 17 | 'vplaned', 18 | sources: [vplaned_library_sources, dataplane_protobuf_c, controller_protobuf_c], 19 | dependencies: [ 20 | czmq_dep, 21 | json_dep, 22 | proto_c_dep, 23 | b64_dep, 24 | jemalloc_dep, 25 | ], 26 | install: true, 27 | soversion: 1 28 | ) 29 | 30 | vplaned_dep = declare_dependency( 31 | link_with: vplaned_library, 32 | include_directories: include_directories('.') 33 | ) 34 | 35 | install_headers( 36 | 'vplaned_cfg.h', 37 | 'vplaned_cstore.h', 38 | 'vplaned_event.h', 39 | 'vplaned.h' 40 | ) 41 | 42 | pkg = import('pkgconfig') 43 | pkg.generate(vplaned_library, filebase: 'libvplaned') 44 | -------------------------------------------------------------------------------- /lib/vplaned.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | */ 9 | #if !defined(__vplaned_h__) 10 | #define __vplaned_h__ 11 | 12 | #include "vplaned_cfg.h" 13 | #include "vplaned_cstore.h" 14 | #include "vplaned_event.h" 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /lib/vplaned_cfg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * External API's for vplaned configuration. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "vplaned_cfg.h" 19 | 20 | static const char *vplaned_config_path = 21 | "ipc:///var/run/vyatta/vplaned-config.socket"; 22 | 23 | struct vplaned_dataplane { 24 | uint16_t id; /* Dataplane ID */ 25 | bool connected; /* Connected (active) dataplane? */ 26 | bool local; /* Local dataplane? */ 27 | char url[0]; /* ZMQ console address */ 28 | }; 29 | 30 | bool 31 | vplaned_dp_is_local(const struct vplaned_dataplane *dp) 32 | { 33 | return dp->local; 34 | } 35 | 36 | bool 37 | vplaned_dp_is_connected(const struct vplaned_dataplane *dp) 38 | { 39 | return dp->connected; 40 | } 41 | 42 | uint16_t 43 | vplaned_dp_id(const struct vplaned_dataplane *dp) 44 | { 45 | return dp->id; 46 | } 47 | 48 | const char * 49 | vplaned_dp_console(const struct vplaned_dataplane *dp) 50 | { 51 | return dp->url; 52 | } 53 | 54 | void 55 | vplaned_dp_destroy(struct vplaned_dataplane **dp) 56 | { 57 | free(*dp); 58 | *dp = NULL; 59 | } 60 | 61 | static void 62 | parse_json_dataplane(const char *json, int dpid, bool only_connected, 63 | bool first, zlist_t *list) 64 | { 65 | json_object *jobj_root, *jobj_dataplanes, *jobj_dp, *jobj_id, *jobj_url, 66 | *jobj; 67 | int index, count = 0; 68 | struct vplaned_dataplane *dp; 69 | 70 | if (!json) 71 | return; 72 | 73 | jobj_root = json_tokener_parse(json); 74 | if (!jobj_root) 75 | return; 76 | 77 | if (json_object_object_get_ex(jobj_root, "dataplanes", 78 | &jobj_dataplanes) && 79 | json_object_is_type(jobj_dataplanes, json_type_array)) 80 | count = json_object_array_length(jobj_dataplanes); 81 | 82 | for (index = 0; index < count; index++) { 83 | bool connected; 84 | 85 | jobj_dp = json_object_array_get_idx(jobj_dataplanes, index); 86 | 87 | if (!(json_object_object_get_ex(jobj_dp, "id", &jobj_id) 88 | && json_object_is_type(jobj_id, json_type_int))) 89 | continue; 90 | 91 | if ((dpid >= 0) && (dpid != json_object_get_int(jobj_id))) 92 | continue; 93 | 94 | connected = json_object_object_get_ex( 95 | jobj_dp, "connected", &jobj) && 96 | json_object_get_boolean(jobj); 97 | 98 | if (only_connected && !connected) 99 | continue; 100 | 101 | if (!(json_object_object_get_ex(jobj_dp, "control", &jobj_url) 102 | && json_object_get_string_len(jobj_url))) 103 | continue; 104 | 105 | dp = malloc(sizeof(*dp) + 106 | json_object_get_string_len(jobj_url) + 1); 107 | if (dp != NULL) { 108 | dp->id = json_object_get_int(jobj_id); 109 | dp->connected = connected; 110 | dp->local = json_object_object_get_ex( 111 | jobj_dp, "local", &jobj) && 112 | json_object_get_boolean(jobj); 113 | strcpy(dp->url, json_object_get_string(jobj_url)); 114 | zlist_append(list, dp); 115 | if (first || (dp->id == dpid)) 116 | break; 117 | } 118 | } 119 | 120 | json_object_put(jobj_root); 121 | } 122 | 123 | static int 124 | get_dataplane(const char *json, int dpid, bool connected, bool first, 125 | struct vplaned_dataplane **dp) 126 | { 127 | zlist_t *list = zlist_new(); 128 | 129 | if (list == NULL) 130 | return -ENOMEM; 131 | 132 | parse_json_dataplane(json, dpid, connected, first, list); 133 | 134 | *dp = zlist_pop(list); 135 | zlist_destroy(&list); 136 | return 0; 137 | } 138 | 139 | int 140 | vplaned_response_get(zsock_t *sock, int timeout, char **json) 141 | { 142 | zmsg_t *msg; 143 | char *sts; 144 | int rc = 0; 145 | 146 | if ((sock == NULL) || (json == NULL)) 147 | return -EINVAL; 148 | 149 | *json = NULL; 150 | 151 | if (timeout > 0) 152 | zsock_set_rcvtimeo(sock, timeout*ZMQ_POLL_MSEC); 153 | 154 | msg = zmsg_recv(sock); 155 | if (msg == NULL) 156 | return -ENODATA; 157 | 158 | sts = zmsg_popstr(msg); 159 | if (sts == NULL) 160 | rc = -EBADMSG; 161 | 162 | if ((rc == 0) && (!streq(sts, "OK"))) 163 | rc = -EBADMSG; 164 | 165 | free(sts); 166 | if (rc == 0) 167 | *json = zmsg_popstr(msg); 168 | 169 | zmsg_destroy(&msg); 170 | return rc; 171 | } 172 | 173 | int 174 | vplaned_dp_get(zsock_t *sock, int timeout, uint16_t dpid, 175 | struct vplaned_dataplane **dp) 176 | { 177 | char *json; 178 | int rc; 179 | 180 | if (dp == NULL) 181 | return -EINVAL; 182 | 183 | *dp = NULL; 184 | rc = vplaned_response_get(sock, timeout, &json); 185 | if (rc == 0) { 186 | rc = get_dataplane(json, dpid, false, false, dp); 187 | free(json); 188 | } 189 | return rc; 190 | } 191 | 192 | int 193 | vplaned_dp_get_first(zsock_t *sock, int timeout, bool connected, 194 | struct vplaned_dataplane **dp) 195 | { 196 | char *json; 197 | int rc; 198 | 199 | if (dp == NULL) 200 | return -EINVAL; 201 | 202 | *dp = NULL; 203 | rc = vplaned_response_get(sock, timeout, &json); 204 | if (rc == 0) { 205 | rc = get_dataplane(json, -1, connected, true, dp); 206 | free(json); 207 | } 208 | return rc; 209 | } 210 | 211 | static int 212 | compare_dpids(void *item1, void *item2) 213 | { 214 | struct vplaned_dataplane *a = item1, *b = item2; 215 | 216 | if (a->id < b->id) 217 | return -1; 218 | 219 | if (a->id > b->id) 220 | return 1; 221 | 222 | return 0; 223 | } 224 | 225 | int 226 | vplaned_dp_get_list(zsock_t *sock, int timeout, bool connected, 227 | zlist_t *list) 228 | { 229 | char *json; 230 | int rc; 231 | 232 | if (list == NULL) 233 | return -EINVAL; 234 | 235 | rc = vplaned_response_get(sock, timeout, &json); 236 | if (rc == 0) { 237 | parse_json_dataplane(json, -1, connected, false, list); 238 | zlist_sort(list, compare_dpids); 239 | free(json); 240 | } 241 | 242 | return rc; 243 | } 244 | 245 | static void 246 | parse_json_controller(const char *json, char **pub, char **req) 247 | { 248 | json_object *jobj_root, *jobj_c, *jobj; 249 | 250 | if (!json) 251 | return; 252 | 253 | jobj_root = json_tokener_parse(json); 254 | if (!jobj_root) 255 | return; 256 | 257 | if (!json_object_object_get_ex(jobj_root, "controller", &jobj_c)) 258 | return; 259 | 260 | if ((pub != NULL) && 261 | json_object_object_get_ex(jobj_c, "publish_url", &jobj) && 262 | json_object_get_string_len(jobj)) 263 | *pub = strdup(json_object_get_string(jobj)); 264 | 265 | if ((req != NULL) && 266 | json_object_object_get_ex(jobj_c, "request_url", &jobj) && 267 | json_object_get_string_len(jobj)) 268 | *req = strdup(json_object_get_string(jobj)); 269 | 270 | json_object_put(jobj_root); 271 | } 272 | 273 | char * 274 | vplaned_ctrl_get_publish_url(zsock_t *sock, int timeout) 275 | { 276 | char *json; 277 | char *url = NULL; 278 | 279 | if (vplaned_response_get(sock, timeout, &json) == 0) { 280 | parse_json_controller(json, &url, NULL); 281 | free(json); 282 | } 283 | return url; 284 | } 285 | 286 | char * 287 | vplaned_ctrl_get_request_url(zsock_t *sock, int timeout) 288 | { 289 | char *json; 290 | char *url = NULL; 291 | 292 | if (vplaned_response_get(sock, timeout, &json) == 0) { 293 | parse_json_controller(json, NULL, &url); 294 | free(json); 295 | } 296 | return url; 297 | } 298 | 299 | static int 300 | vplaned_request_object(zsock_t *sock, const char *obj) 301 | { 302 | if (sock == NULL) 303 | return -EINVAL; 304 | 305 | if (zstr_send(sock, obj) < 0) 306 | return -errno; 307 | return 0; 308 | } 309 | 310 | int 311 | vplaned_request_dataplane(zsock_t *sock) 312 | { 313 | return vplaned_request_object(sock, "GETVPCONFIG"); 314 | } 315 | 316 | int 317 | vplaned_request_config(zsock_t *sock) 318 | { 319 | return vplaned_request_object(sock, "GETCONFIG"); 320 | } 321 | 322 | void 323 | vplaned_disconnect(zsock_t **sock) 324 | { 325 | zsock_destroy(sock); 326 | } 327 | 328 | zsock_t * 329 | __vplaned_connect(const char *path) 330 | { 331 | /* 332 | * Allow test code to specify the IPC socket path 333 | */ 334 | if (path == NULL) 335 | path = vplaned_config_path; 336 | 337 | if ((strncmp(path, "ipc://", 6) == 0) && 338 | (access(path + 6, W_OK|R_OK) < 0)) 339 | return NULL; 340 | 341 | return zsock_new_req(path); 342 | } 343 | 344 | zsock_t * 345 | vplaned_connect(void) 346 | { 347 | return __vplaned_connect(NULL); 348 | } 349 | -------------------------------------------------------------------------------- /lib/vplaned_cfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * External API's for the controller (vplaned). Similar to the 9 | * Vyatta::Dataplane.pm module, the functions allow a user to retrieve 10 | * configuration details about the controller together with details of 11 | * individual dataplane(s). Primarily the console (control) URL in order 12 | * to issue status/show commands. 13 | * 14 | * A typical blocking call pattern might be: 15 | * 16 | * s = vplaned_connect(); 17 | * vplaned_request_dataplane(s); 18 | * struct vplaned_dataplane *dp; 19 | * vplaned_dp_get(sock, TIMEOUT, dpid, &dp); 20 | * if (dp != NULL) 21 | * url = strdup(vplaned_dp_console(dp)); 22 | * vplaned_dp_destroy(&dp); 23 | * vplaned_disconnect(&sock); 24 | * 25 | * 26 | * 27 | * free(url); 28 | * 29 | * A typical non-blocking call pattern might be: 30 | * 31 | * s = vplaned_connect(); 32 | * vplaned_request_dataplane(s); 33 | * 34 | * 35 | * 36 | * struct vplaned_dataplane *dp; 37 | * vplaned_dp_get_first(s, 0, true, &dp); 38 | * 39 | * if (dp != NULL) 40 | * url = strdup(vplaned_dp_console(dp)); 41 | * vplaned_dp_destroy(&dp); 42 | * vplaned_disconnect(&s); 43 | * 44 | * 45 | * 46 | * free(url); 47 | */ 48 | #if !defined(__vplaned_cfg_h__) 49 | #define __vplaned_cfg_h__ 50 | 51 | /* 52 | * DO NOT USE: testing only 53 | */ 54 | extern zsock_t * 55 | __vplaned_connect(const char *path); 56 | 57 | /* 58 | * Connect to the controller daemon (vplaned) 59 | */ 60 | extern zsock_t * 61 | vplaned_connect(void); 62 | 63 | /* 64 | * Disconnect & destroy the connection to the controller daemon. 65 | */ 66 | extern void 67 | vplaned_disconnect(zsock_t **sock); 68 | 69 | /* 70 | * Request the JSON dataplane object from the controller - details of 71 | * every configured dataplane instance. 72 | * 73 | * sock - ZMQ connection to the controller 74 | * 75 | * Returns 0 on success and a negative (< 0) errno value on failure 76 | */ 77 | extern int 78 | vplaned_request_dataplane(zsock_t *sock); 79 | 80 | /* 81 | * Request the JSON configuration object from the controller - details 82 | * of the controller configuration. 83 | * 84 | * sock - ZMQ connection to the controller 85 | * 86 | * Returns 0 on success and a negative (< 0) errno value on failure 87 | */ 88 | extern int 89 | vplaned_request_config(zsock_t *sock); 90 | 91 | /* 92 | * Retrieve the previously requested (raw) JSON object from the 93 | * controller. 94 | * 95 | * sock - ZMQ connection to the controller 96 | * 97 | * timeout - ZMQ receive timeout value (millseconds), 0 => non-blocking 98 | * 99 | * json - Returned JSON string. Caller owns string, use free() when 100 | * finished 101 | * 102 | * Returns 0 on success and a negative (< 0) errno value on failure 103 | */ 104 | extern int 105 | vplaned_response_get(zsock_t *sock, int timeout, char **json); 106 | 107 | /* 108 | * Having previously issued a request for the controller configuration, 109 | * retrieve the ZeroMQ address of the publish or request sockets. 110 | * 111 | * sock - ZMQ connection to the controller 112 | * 113 | * timeout - ZMQ receive timeout value (millseconds), 0 => non-blocking 114 | * 115 | * Caller owns returned string, use free() when finished. 116 | */ 117 | extern char * 118 | vplaned_ctrl_get_publish_url(zsock_t *sock, int timeout); 119 | extern char * 120 | vplaned_ctrl_get_request_url(zsock_t *sock, int timeout); 121 | 122 | /* 123 | * Dataplane entry returned by the following vplane_dp_xxx() 124 | * functions. Use the accessor functions to derive details of the 125 | * element. Use vplane_dp_destroy() when finished with the object. 126 | */ 127 | struct vplaned_dataplane; 128 | 129 | /* 130 | * Process the previous dataplane request and return the the dataplane 131 | * with the specified ID 132 | * 133 | * sock - ZMQ connection to the controller 134 | * 135 | * timeout - ZMQ receive timeout value (millseconds), 0 => non-blocking 136 | * 137 | * dpid - Dataplane ID 138 | * 139 | * dp - Returned dataplane object (or NULL if none found). Use 140 | * vplane_dp_destroy() when finished. 141 | * 142 | * Returns 0 on success and a negative (< 0) errno value on failure 143 | */ 144 | extern int 145 | vplaned_dp_get(zsock_t *sock, int timeout, uint16_t dpid, 146 | struct vplaned_dataplane **dp); 147 | 148 | /* 149 | * Process the previous dataplane request and return the first (any) 150 | * dataplane element. 151 | * 152 | * sock - ZMQ connection to the controller 153 | * 154 | * timeout - ZMQ receive timeout value (millseconds), 0 => non-blocking 155 | * 156 | * connected - Only return a connected dataplane 157 | * 158 | * dp - Returned dataplane object (or NULL if none found). Use 159 | * vplane_dp_destroy() when finished. 160 | * 161 | * Returns 0 on success and a negative (< 0) errno value on failure 162 | */ 163 | extern int 164 | vplaned_dp_get_first(zsock_t *sock, int timeout, bool connected, 165 | struct vplaned_dataplane **dp); 166 | 167 | /* 168 | * Process the previous dataplane request and return a list of all 169 | * dataplanes. 170 | * 171 | * sock - ZMQ connection to the controller 172 | * 173 | * timeout - ZMQ receive timeout value (millseconds), 0 => non-blocking 174 | * 175 | * connected - Only return a list of connected dataplanes 176 | * 177 | * list - List of dataplane objects, maybe empty if no dataplanes exist. Use 178 | * vplane_dp_destroy() when finished. 179 | * 180 | * Returns 0 on success and a negative (< 0) errno value on failure 181 | */ 182 | extern int 183 | vplaned_dp_get_list(zsock_t *sock, int timeout, bool connected, 184 | zlist_t *list); 185 | 186 | /* 187 | * Accessor functions for an individual dataplane element 188 | */ 189 | 190 | extern bool 191 | vplaned_dp_is_local(const struct vplaned_dataplane *dp); 192 | 193 | extern bool 194 | vplaned_dp_is_connected(const struct vplaned_dataplane *dp); 195 | 196 | extern uint16_t 197 | vplaned_dp_id(const struct vplaned_dataplane *dp); 198 | 199 | extern const char * 200 | vplaned_dp_console(const struct vplaned_dataplane *dp); 201 | 202 | extern void 203 | vplaned_dp_destroy(struct vplaned_dataplane **dp); 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /lib/vplaned_cstore.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020, AT&T Intellectual Property. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: LGPL-2.1-only 6 | * 7 | * External API's for vplaned cstore database. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "vplaned_cstore.h" 19 | 20 | #include "DataplaneEnvelope.pb-c.h" 21 | #include "VPlanedEnvelope.pb-c.h" 22 | 23 | static const char *vplaned_cstore_path = 24 | "ipc:///var/run/vyatta/vplaned.socket"; 25 | 26 | zsock_t * 27 | __vplaned_cstore_connect(const char *path) 28 | { 29 | if (path == NULL) 30 | path = vplaned_cstore_path; 31 | 32 | if ((strncmp(path, "ipc://", 6) == 0) && 33 | (access(path + 6, W_OK|R_OK) < 0)) 34 | return NULL; 35 | 36 | return zsock_new_req(path); 37 | } 38 | 39 | zsock_t * 40 | vplaned_cstore_connect(void) 41 | { 42 | return __vplaned_cstore_connect(NULL); 43 | } 44 | 45 | void 46 | vplaned_cstore_disconnect(zsock_t **sock) 47 | { 48 | zsock_destroy(sock); 49 | } 50 | 51 | struct cstore_args { 52 | char **markerstr; 53 | char *savestr; 54 | const char *cmd; 55 | const char *action; 56 | const char *interface; 57 | bool protobuf; 58 | }; 59 | 60 | static json_object * 61 | vplaned_cstore_format(const char *token, const struct cstore_args *args) 62 | { 63 | json_object *parent; 64 | 65 | parent = json_object_new_object(); 66 | if (parent == NULL) 67 | return NULL; 68 | 69 | if (token != NULL) { 70 | json_object *child; 71 | 72 | child = vplaned_cstore_format( 73 | strtok_r(NULL, " ", args->markerstr), 74 | args); 75 | if (child != NULL) 76 | json_object_object_add(parent, token, child); 77 | else { 78 | json_object_put(parent); 79 | parent = NULL; 80 | } 81 | return parent; 82 | } 83 | 84 | json_object *cmdstr = json_object_new_string(args->cmd); 85 | json_object *intfstr = json_object_new_string(args->interface); 86 | json_object *protobufstr = NULL; 87 | 88 | if ((cmdstr != NULL) && (intfstr != NULL)) { 89 | char actbuf[64]; 90 | 91 | snprintf(actbuf, sizeof(actbuf), "__%s__", args->action); 92 | json_object_object_add(parent, actbuf, cmdstr); 93 | json_object_object_add(parent, "__INTERFACE__", intfstr); 94 | if (args->protobuf) { 95 | protobufstr = json_object_new_boolean(args->protobuf); 96 | if (protobufstr) 97 | json_object_object_add(parent, "__PROTOBUF__", 98 | protobufstr); 99 | } 100 | return parent; 101 | } 102 | 103 | json_object_put(parent); 104 | json_object_put(cmdstr); 105 | json_object_put(intfstr); 106 | return NULL; 107 | } 108 | 109 | static int 110 | vplaned_cstore_request_internal(zsock_t *sock, const char *path, 111 | const char *cmd, const char *interface, 112 | const char *action, bool protobuf) 113 | { 114 | struct cstore_args args; 115 | 116 | if ((sock == NULL) || 117 | (path == NULL) || 118 | (cmd == NULL) || 119 | (action == NULL)) 120 | return -EINVAL; 121 | 122 | args.cmd = cmd; 123 | args.action = action; 124 | args.protobuf = protobuf; 125 | if (interface == NULL) 126 | args.interface = "ALL"; 127 | else 128 | args.interface = interface; 129 | 130 | args.savestr = NULL; 131 | args.markerstr = &args.savestr; 132 | 133 | char *key = strdup(path); 134 | json_object *jobj = NULL; 135 | 136 | if (key != NULL) 137 | jobj = vplaned_cstore_format( 138 | strtok_r(key, " ", args.markerstr), 139 | &args); 140 | 141 | free(key); 142 | if (jobj == NULL) 143 | return -ENOMEM; 144 | 145 | const char *str; 146 | int rc = 0; 147 | 148 | str = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN); 149 | if (str == NULL) 150 | rc = -ENOMEM; 151 | 152 | if ((rc == 0) && (zstr_send(sock, str) < 0)) 153 | rc = -EIO; 154 | 155 | json_object_put(jobj); 156 | return rc; 157 | } 158 | 159 | /* 160 | * Build up pb json cstore message. It is of the form: 161 | * 162 | * { "____" : "protobuf ", 163 | * "__PROTOBUF__" : true, 164 | * "__INTERFACE__" : 165 | * } 166 | * 167 | * The base64_encoded message is formed by doing the following: 168 | * - wrap the given cmd in the DataplaneEnvelope protobuf 169 | * - then wrap that in the VPlanedEnvelope 170 | * - then base64 encode the result. 171 | */ 172 | int 173 | vplaned_cstore_pb_request(zsock_t *sock, const char *path, void *cmd, 174 | int cmd_len, 175 | const char *cmd_name, 176 | const char *interface, const char *action) 177 | { 178 | DataplaneEnvelope dp_msg = DATAPLANE_ENVELOPE__INIT; 179 | VPlanedEnvelope vplaned_msg = VPLANED_ENVELOPE__INIT; 180 | int dp_len; 181 | void *dp_buf; 182 | int vplaned_len; 183 | void *vplaned_buf; 184 | base64_encodestate encode_state; 185 | char *base64_outbuf; 186 | char *base64_outbuf_ptr; 187 | int count; 188 | char *pb_str = "protobuf "; 189 | int extra_len = strlen(pb_str); 190 | 191 | if ((sock == NULL) || 192 | (path == NULL) || 193 | (cmd == NULL) || 194 | (cmd_name == NULL) || 195 | (action == NULL)) 196 | return -EINVAL; 197 | 198 | if (strcmp(action, "SET") == 0) 199 | vplaned_msg.action = VPLANED_ENVELOPE__ACTION__SET; 200 | else if ( strcmp(action, "DELETE") == 0) 201 | vplaned_msg.action = VPLANED_ENVELOPE__ACTION__DELETE; 202 | else 203 | return -EINVAL; 204 | 205 | /* 206 | * build the base64 encoded message, then call vplaned_store_request 207 | * which will build the json. 208 | */ 209 | 210 | /* Build up dataplane envelope. */ 211 | dp_msg.type = (char *)cmd_name; 212 | dp_msg.msg.data = cmd; 213 | dp_msg.msg.len = cmd_len; 214 | dp_len = dataplane_envelope__get_packed_size(&dp_msg); 215 | dp_buf = malloc(dp_len); 216 | if (!dp_buf) 217 | return -ENOMEM; 218 | dataplane_envelope__pack(&dp_msg, dp_buf); 219 | 220 | /* Build up vplaned envelope. */ 221 | vplaned_msg.key = (char *)path; 222 | vplaned_msg.interface = (char *)interface; 223 | vplaned_msg.msg.data = dp_buf; 224 | vplaned_msg.msg.len = dp_len; 225 | vplaned_len = vplaned_envelope__get_packed_size(&vplaned_msg); 226 | vplaned_buf = malloc(vplaned_len); 227 | if (!vplaned_buf) { 228 | free(dp_buf); 229 | return -ENOMEM; 230 | } 231 | vplaned_envelope__pack(&vplaned_msg, vplaned_buf); 232 | free(dp_buf); 233 | 234 | /* Convert to base64 */ 235 | 236 | /* More mem than needed but at least it is always enough */ 237 | base64_outbuf = calloc(1, vplaned_len * 2 + 1 + extra_len); 238 | if (!base64_outbuf) { 239 | free(vplaned_buf); 240 | return -ENOMEM; 241 | } 242 | 243 | /* Store 'protobuf ' at the start of the string. */ 244 | snprintf(base64_outbuf, extra_len + 1, "%s", pb_str); 245 | base64_outbuf_ptr = base64_outbuf + extra_len; 246 | 247 | /* And then store the base64 encoded message */ 248 | base64_init_encodestate(&encode_state); 249 | count = base64_encode_block(vplaned_buf, vplaned_len, base64_outbuf_ptr, 250 | &encode_state); 251 | base64_outbuf_ptr += count; 252 | count = base64_encode_blockend(base64_outbuf_ptr, &encode_state); 253 | base64_outbuf_ptr += count; 254 | *base64_outbuf_ptr = 0; 255 | 256 | return vplaned_cstore_request_internal(sock, path, base64_outbuf, 257 | interface, action, true); 258 | } 259 | 260 | int 261 | vplaned_cstore_request(zsock_t *sock, const char *path, const char *cmd, 262 | const char *interface, const char *action) 263 | { 264 | return vplaned_cstore_request_internal(sock, path, cmd, 265 | interface, action, false); 266 | } 267 | 268 | int 269 | vplaned_cstore_response(zsock_t *sock, int timeout) 270 | { 271 | zmsg_t *msg; 272 | char *sts; 273 | int rc = 0; 274 | 275 | if (sock == NULL) 276 | return -EINVAL; 277 | 278 | if (timeout > 0) 279 | zsock_set_rcvtimeo(sock, timeout*ZMQ_POLL_MSEC); 280 | 281 | msg = zmsg_recv(sock); 282 | if (msg == NULL) 283 | return -ENODATA; 284 | 285 | sts = zmsg_popstr(msg); 286 | if (sts == NULL) 287 | rc = -EBADMSG; 288 | 289 | if ((rc == 0) && (!streq(sts, "OK"))) 290 | rc = -EPROTO; 291 | 292 | free(sts); 293 | zmsg_destroy(&msg); 294 | return rc; 295 | } 296 | 297 | int 298 | __vplaned_cstore_store(const char *path, const char *cmd, const char *interface, 299 | const char *action, int timeout, const char *spath) 300 | { 301 | zsock_t *s; 302 | int rc; 303 | 304 | s = __vplaned_cstore_connect(spath); 305 | if (s == NULL) 306 | return -ENOENT; 307 | 308 | rc = vplaned_cstore_request(s, path, cmd, interface, action); 309 | if (rc == 0) 310 | rc = vplaned_cstore_response(s, timeout); 311 | 312 | vplaned_cstore_disconnect(&s); 313 | return rc; 314 | } 315 | 316 | int 317 | vplaned_cstore_store(const char *path, const char *cmd, const char *interface, 318 | const char *action, int timeout) 319 | { 320 | return __vplaned_cstore_store(path, cmd, interface, action, timeout, 321 | NULL); 322 | } 323 | -------------------------------------------------------------------------------- /lib/vplaned_cstore.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020, AT&T Intellectual Property. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: LGPL-2.1-only 6 | * 7 | * External API's for the controller (vplaned). Similar to the 8 | * Vyatta::VPlaned.pm (and vyatta.vplaned.py), the API's allow an 9 | * application to inject configuration objects into the Controller 10 | * cstore database. 11 | * 12 | * The module provides both blocking and non-blocking functions. A 13 | * typical blocking call pattern might be: 14 | * 15 | * err = vplaned_cstore_store( 16 | * "security firewall name test1 default-action", 17 | * "npf-cfg add fw:test1 10000 action=accept", 18 | * NULL, "SET", 19 | * TIMEOUT); 20 | * 21 | * a non-blocking call pattern might be: 22 | * 23 | * s = vplaned_cstore_connect(); 24 | * err = vplaned_cstore_request( 25 | * s, 26 | * "security firewall name test1 default-action", 27 | * "npf-cfg add fw:test1 10000 action=accept", 28 | * NULL, "SET"); 29 | * 30 | * 31 | * 32 | * err = vplaned_cstore_response(s, 0); 33 | * vplaned_cstore_disconnect(&s); 34 | * 35 | */ 36 | #if !defined(__vplaned_cstore_h__) 37 | #define __vplaned_cstore_h__ 38 | 39 | /* 40 | * DO NOT USE: testing only 41 | */ 42 | extern zsock_t * 43 | __vplaned_cstore_connect(const char *path); 44 | extern int 45 | __vplaned_cstore_store(const char *path, const char *cmd, const char *interface, 46 | const char *action, int timeout, const char *spath); 47 | 48 | /* 49 | * Connect to the controller daemon (vplaned) 50 | */ 51 | extern zsock_t * 52 | vplaned_cstore_connect(void); 53 | 54 | /* 55 | * Disconnect & destroy the connection to the controller daemon. 56 | */ 57 | extern void 58 | vplaned_cstore_disconnect(zsock_t **sock); 59 | 60 | /* 61 | * Pass a command object to the cstore subsystem inside vplaned 62 | * 63 | * sock - ZMQ connection to the controller 64 | * path - Path to stored object (the "key") 65 | * cmd - Configuration string to be stored with "key" (the "value") 66 | * interface - Optional interface name used to augment the "key", if 67 | * absent, "ALL" is used. 68 | * action - How the "key"/"value" pair is to be processed: "SET" or "DELETE" 69 | * 70 | * Returns 0 on success and a negative (< 0) errno value on failure 71 | */ 72 | extern int 73 | vplaned_cstore_request(zsock_t *sock, const char *path, const char *cmd, 74 | const char *interface, const char *action); 75 | 76 | /* 77 | * Pass a command protobuf object to the cstore subsystem inside vplaned 78 | * 79 | * sock - ZMQ connection to the controller 80 | * path - Path to stored object (the "key") 81 | * cmd - Configuration string to be stored with "key" (the "value") 82 | * This is a protobuf encoded message. 83 | * cmd_len - The length of 'cmd' 84 | * cmd_name - the name of the handler when the msg reaches the dataplane. 85 | * interface - Optional interface name used to augment the "key", if 86 | * absent, "ALL" is used. 87 | * action - How the "key"/"value" pair is to be processed: "SET" or "DELETE" 88 | * 89 | * Returns 0 on success and a negative (< 0) errno value on failure 90 | */ 91 | extern int 92 | vplaned_cstore_pb_request(zsock_t *sock, const char *path, void *cmd, 93 | int cmd_len, 94 | const char *cmd_name, 95 | const char *interface, const char *action); 96 | 97 | 98 | /* 99 | * Retrieve the result of the previous cstore request from the controller. 100 | * 101 | * sock - ZMQ connection to the controller 102 | * timeout - How long to block waiting for the response from the 103 | * controller (millseconds). A value of 0 indicates an 104 | * indefinite wait 105 | * 106 | * Returns 0 on success and a negative (< 0) errno value on failure 107 | */ 108 | extern int 109 | vplaned_cstore_response(zsock_t *sock, int timeout); 110 | 111 | /* 112 | * Pass a command object to the cstore subsystem and wait for the 113 | * response, i.e. a synchronous wrapper around 114 | * vplaned_cstore_connect(), vplaned_cstore_request() & 115 | * vplaned_cstore_response(), vplaned_cstore_disconnect(). 116 | * 117 | * path - Path to stored object (the "key") 118 | * cmd - Configuration string to be stored with "key" (the "value") 119 | * interface - Optional interface name used to augment the "key", if 120 | * absent, "ALL" is used. 121 | * action - How the "key"/"value" pair is to be processed: "SET" or "DELETE" 122 | * timeout - How long to block waiting for the response from the 123 | * controller (millseconds). A value of 0 indicates an 124 | * indefinite wait 125 | * 126 | */ 127 | extern int 128 | vplaned_cstore_store(const char *path, const char *cmd, const char *interface, 129 | const char *action, int timeout); 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /lib/vplaned_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: LGPL-2.1-only 6 | * 7 | * External API's for vplaned event publisher. 8 | */ 9 | 10 | #include 11 | #include "vplaned_event.h" 12 | 13 | zsock_t * 14 | vplaned_event_subscribe(const char *topic) 15 | { 16 | const char *event_path = "ipc:///var/run/vyatta/vplaned-event.pub"; 17 | 18 | if (access(event_path + 6, R_OK) < 0) 19 | return NULL; 20 | 21 | return zsock_new_sub(event_path, topic); 22 | } 23 | 24 | void 25 | vplaned_event_disconnect(zsock_t **sock) 26 | { 27 | zsock_destroy(sock); 28 | } 29 | -------------------------------------------------------------------------------- /lib/vplaned_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: LGPL-2.1-only 6 | * 7 | * External API's for the controller (vplaned). 8 | */ 9 | #if !defined(__vplaned_event_h__) 10 | #define __vplaned_event_h__ 11 | 12 | /* 13 | * Create a subscriber connected to the controller daemon's event 14 | * publisher and subscribe to the given topic. 15 | */ 16 | extern zsock_t * 17 | vplaned_event_subscribe(const char *topic); 18 | 19 | /* 20 | * Disconnect & destroy the connection to the controller daemon. 21 | */ 22 | extern void 23 | vplaned_event_disconnect(zsock_t **sock); 24 | #endif 25 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | project('vyatta-controller', ['c', 'cpp'], 5 | default_options: [ 6 | 'buildtype=debug', 7 | 'werror=true', 8 | 'warning_level=2', 9 | 'c_std=gnu11' 10 | ] 11 | ) 12 | 13 | cc = meson.get_compiler('c') 14 | add_project_arguments( 15 | '-Wall', '-Wextra', '-Werror', 16 | '-Wmissing-prototypes', '-Wredundant-decls', 17 | '-D_GNU_SOURCE', 18 | cc.get_supported_arguments([ 19 | '-Wno-stringop-overflow', 20 | '-Wno-stringop-truncation', 21 | '-Wno-format-truncation' 22 | ]), 23 | language: 'c' 24 | ) 25 | 26 | czmq_dep = dependency('libczmq') 27 | zmq_dep = dependency('libzmq') 28 | json_dep = dependency('json-c') 29 | mnl_dep = dependency('libmnl') 30 | libedit_dep = dependency('libedit') 31 | vrfmanager_dep = dependency('libvrfmanager-vyatta') 32 | systemd_dep = dependency('libsystemd') 33 | proto_c_dep = dependency('libprotobuf-c') 34 | proto_cxx_dep = dependency('protobuf') 35 | jemalloc_dep = dependency('jemalloc', required : get_option('use_jemalloc')) 36 | 37 | cc = meson.get_compiler('c') 38 | b64_dep = cc.find_library('b64', required: true) 39 | ini_dep = cc.find_library('inih', required: true) 40 | 41 | subdir('protobuf') 42 | subdir('lib') 43 | subdir('daemon') 44 | subdir('client') 45 | subdir('snmp') 46 | subdir('test/example') 47 | subdir('test/module') 48 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('use_jemalloc', type : 'feature', value : 'disabled') 2 | -------------------------------------------------------------------------------- /protobuf/VPlanedEnvelope.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | // 4 | // SPDX-License-Identifier: LGPL-2.1-only 5 | // 6 | 7 | syntax="proto2"; 8 | 9 | option go_package = "github.com/danos/vyatta-controller/protobuf/go/VPlanedEnvelope"; 10 | 11 | message VPlanedEnvelope { 12 | required string key = 1; //persistence key 13 | optional string interface = 2; //interface hint 14 | enum Action { 15 | SET = 0; 16 | DELETE = 1; 17 | UPDATE = 2; 18 | } 19 | required Action action = 3; //configuration action 20 | required bytes msg = 4; //Dataplane envelope + message 21 | } -------------------------------------------------------------------------------- /protobuf/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | protobuf_source = files( 5 | 'VPlanedEnvelope.proto' 6 | ) 7 | 8 | install_data(protobuf_source, 9 | install_dir: get_option('datadir') / 'vyatta-dataplane' / 'protobuf' 10 | ) 11 | 12 | protoc = find_program('protoc') 13 | 14 | controller_protobuf_c = custom_target('controller_protobuf_c', 15 | input: protobuf_source, 16 | output: ['VPlanedEnvelope.pb-c.c', 'VPlanedEnvelope.pb-c.h'], 17 | command: [protoc, '--proto_path=@CURRENT_SOURCE_DIR@', '--c_out=@OUTDIR@', '@INPUT@'], 18 | ) 19 | 20 | controller_protobuf_cxx = custom_target('controller_protobuf_cxx', 21 | input: protobuf_source, 22 | output: ['VPlanedEnvelope.pb.cc', 'VPlanedEnvelope.pb.h'], 23 | command: [protoc, '--proto_path=@CURRENT_SOURCE_DIR@', '--cpp_out=@OUTDIR@', '@INPUT@'], 24 | install: true, 25 | install_dir: [false, get_option('includedir') / 'vyatta-controller' / 'proto'], 26 | ) 27 | 28 | controller_proto_cxx_library = shared_library( 29 | 'vyatta-controller-proto', 30 | sources: [controller_protobuf_cxx], 31 | dependencies: [proto_cxx_dep], 32 | install: true, 33 | soversion: 1 34 | ) 35 | 36 | pkg = import('pkgconfig') 37 | pkg.generate(controller_proto_cxx_library, subdirs: 'vyatta-controller/proto') 38 | 39 | protobuf_py = custom_target('controller_protobuf_py', 40 | input: protobuf_source, 41 | output: ['VPlanedEnvelope_pb2.py'], 42 | command: [protoc, '--proto_path=@CURRENT_SOURCE_DIR@', '--python_out=@OUTDIR@', '@INPUT@'], 43 | install: true, 44 | install_dir: 'lib/python3/dist-packages/vyatta/proto' 45 | ) 46 | 47 | perl_generator = files('../scripts/vyatta-generate-pb-perl.pl') 48 | 49 | protobuf_perl = custom_target('controller_protobuf_perl', 50 | input: protobuf_source, 51 | output: ['VPlanedEnvelope.pm'], 52 | command: [perl_generator, '@INPUT@', '@OUTDIR@', '@CURRENT_SOURCE_DIR@'], 53 | install: true, 54 | install_dir: 'share/perl5/vyatta/proto' 55 | ) 56 | 57 | protobuf_go = custom_target('controller_protobuf_go', 58 | input: protobuf_source, 59 | output: ['VPlanedEnvelope.pb.go'], 60 | command: [protoc, '--proto_path=@CURRENT_SOURCE_DIR@', '--go_out=paths=source_relative:@OUTDIR@', '@INPUT@'], 61 | install: true, 62 | install_dir: 'share/gocode/src/github.com/danos/vyatta-controller/protobuf/go/VPlanedEnvelope' 63 | ) 64 | 65 | 66 | -------------------------------------------------------------------------------- /scripts/vyatta-generate-pb-perl.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Carp; 7 | 8 | use MIME::Base64; 9 | use Google::ProtocolBuffers; 10 | 11 | my $target_dir = $ARGV[1]; 12 | my $proto_dir = $ARGV[2]; 13 | `mkdir -p $target_dir`; 14 | 15 | if (substr($ARGV[0], -6) eq ".proto") { 16 | 17 | my %options; 18 | $options{include_dir} = $proto_dir; 19 | 20 | my $pb_pm = substr($ARGV[0], 0, length($ARGV[0]) - 6) . ".pm"; 21 | 22 | $options{generate_code} = $target_dir . "/$pb_pm"; 23 | $options{create_accessors} = 1; 24 | Google::ProtocolBuffers->parsefile($proto_dir . "/$ARGV[0]", \%options); 25 | } 26 | 27 | exit 0; 28 | -------------------------------------------------------------------------------- /snmp/.gitignore: -------------------------------------------------------------------------------- 1 | vplane-snmp -------------------------------------------------------------------------------- /snmp/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | executable( 5 | 'vplane-snmp', 6 | sources: ['vplane-snmp.c'], 7 | dependencies: [ 8 | czmq_dep, 9 | json_dep, 10 | vplaned_dep 11 | ], 12 | install: true, 13 | install_dir: '/opt/vyatta/sbin' 14 | ) 15 | -------------------------------------------------------------------------------- /test/example/bfdtest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-only 5 | * 6 | * bfdtest - Create/update/delete BFD sessions in the dataplane via the 7 | * cstore and monitor state updates via the event socket. 8 | * 9 | * Example usage: 10 | * 11 | * Create a session with source 1.2.3.4, dest 1.2.3.5 via interface dp0s2 12 | * and monitor for state updates: 13 | * 14 | * bfdtest -s 1.2.3.4 -d 1.2.3.5 -i dp0s2 -m 15 | * 16 | * Delete the above session: 17 | * 18 | * bfdtest -s 1.2.3.4 -d 1.2.3.5 -i dp0s2 -x 19 | * 20 | * Create a session, specifying client id: 21 | * 22 | * bfdtest -s 1.2.3.4 -d 1.2.3.5 -i dp0s2 -c 2 23 | * 24 | * Monitor state updates for current sessions: 25 | * 26 | * bfdtest -m 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include "vplaned.h" 34 | 35 | /* Defaults */ 36 | #define TX_DESIRED 300000 37 | #define RX_REQUIRED 300000 38 | #define DET_MULT 3 39 | #define NO_ADMIN_DOWN 0 40 | #define CLIENT_ID 1 41 | #define VRF_DEFAULT_ID 1 42 | 43 | enum bfd_state { 44 | BFD_STATE_ADMIN_DOWN = 0, 45 | BFD_STATE_DOWN = 1, 46 | BFD_STATE_INIT = 2, 47 | BFD_STATE_UP = 3, 48 | BFD_NUM_STATES 49 | }; 50 | 51 | static const char *bfd_state_strings[BFD_NUM_STATES] = { 52 | "Admin Down", 53 | "Down", 54 | "Init", 55 | "Up" 56 | }; 57 | 58 | static const char *bfd_state_name(enum bfd_state state) 59 | { 60 | if (state >= BFD_NUM_STATES) 61 | return ""; 62 | 63 | return bfd_state_strings[state]; 64 | } 65 | 66 | static struct option longopts[] = { 67 | { "source", required_argument, NULL, 's' }, 68 | { "dest", required_argument, NULL, 'd' }, 69 | { "vrf", required_argument, NULL, 'v' }, 70 | { "interface", required_argument, NULL, 'i' }, 71 | { "client-id", required_argument, NULL, 'c' }, 72 | { "password", required_argument, NULL, 'p' }, 73 | { "monitor", no_argument, NULL, 'm' }, 74 | { "admin-down", no_argument, NULL, 'a' }, 75 | { "help", no_argument, NULL, 'h' }, 76 | { NULL, 0, NULL, 0 } 77 | }; 78 | 79 | static const char *progname; 80 | 81 | static void usage(void) 82 | { 83 | printf("Usage: %s [OPTION...]\n\n" 84 | "vPlaned BFD test.\n\n" 85 | "-s, --source Session source address (required)\n" 86 | "-d, --dest Session dest address (required)\n" 87 | "-v, --vrf VRF id\n" 88 | "-i, --interface Interface (single hop)\n" 89 | "-c, --client-id Client id\n" 90 | "-t, --tx-des Min tx desired interval (ms)\n" 91 | "-r, --rx-req Min rx required interval (ms)\n" 92 | "-u, --det-mult Detect multiplier\n" 93 | "-p, --password Simple authentication password\n" 94 | "-m, --monitor Monitor state update events\n" 95 | "-a, --admin-down Hold session down\n" 96 | "-x, --delete Delete the session\n" 97 | "-h, --help Display help and exit\n\n", 98 | progname); 99 | exit(EXIT_FAILURE); 100 | } 101 | 102 | int main(int argc, char **argv) 103 | { 104 | uint32_t ifi = 0; 105 | uint32_t clid = CLIENT_ID; 106 | uint32_t vrfid = VRF_DEFAULT_ID; 107 | uint32_t admin_down = NO_ADMIN_DOWN; 108 | uint32_t tx_des = TX_DESIRED; 109 | uint32_t rx_req = RX_REQUIRED; 110 | uint32_t det_mult = DET_MULT; 111 | int flag; 112 | bool monitor = false; 113 | char *src = NULL, *dest = NULL, *intf = NULL, *passwd = NULL; 114 | char cstore_key[100]; 115 | char cstore_cmd[100]; 116 | char auth[30] = {0}; 117 | zsock_t *sub; 118 | bool add = false; 119 | bool del = false; 120 | 121 | progname = argv[0]; 122 | 123 | while ((flag = getopt_long(argc, argv, "ac:s:d:v:i:p:mx", 124 | longopts, 0)) != EOF) { 125 | switch (flag) { 126 | case 'a': 127 | admin_down = 1; 128 | break; 129 | case 'c': 130 | clid = atoi(optarg); 131 | break; 132 | case 'd': 133 | dest = optarg; 134 | break; 135 | case 's': 136 | src = optarg; 137 | break; 138 | case 'v': 139 | vrfid = atoi(optarg); 140 | break; 141 | case 't': 142 | tx_des = atoi(optarg) * 1000; 143 | break; 144 | case 'r': 145 | rx_req = atoi(optarg) * 1000; 146 | break; 147 | case 'u': 148 | det_mult = atoi(optarg); 149 | break; 150 | case 'i': 151 | intf = optarg; 152 | ifi = if_nametoindex(intf); 153 | if (!ifi) { 154 | printf("Interface %s not found\n", intf); 155 | exit(EXIT_FAILURE); 156 | } 157 | break; 158 | case 'p': 159 | passwd = optarg; 160 | break; 161 | case 'm': 162 | monitor = true; 163 | break; 164 | case 'x': 165 | del = true; 166 | add = false; 167 | break; 168 | default: 169 | usage(); 170 | } 171 | } 172 | 173 | /* If src and dest addrs given, assume session add/update */ 174 | if (src && dest && !add && !del) 175 | add = true; 176 | 177 | /* If adding or deleting, must have src/dest */ 178 | if ((add || del) && (!src || !dest)) 179 | usage(); 180 | 181 | if (add || del) 182 | printf("%s%s session %u from %s to %s, " 183 | "via intf %s(%d) with passwd %s\n", 184 | add ? "Update" : "", del ? "Delete" : "", 185 | clid, src, dest, 186 | intf ? intf : "none", 187 | ifi, 188 | passwd ? passwd : "none"); 189 | 190 | if (monitor) 191 | sub = vplaned_event_subscribe("BFD"); 192 | 193 | if (add || del) 194 | snprintf(cstore_key, sizeof(cstore_key), 195 | "bfdsess %u %u %s %s", ifi, vrfid, src, dest); 196 | if (add) { 197 | if (passwd) 198 | snprintf(auth, sizeof(auth), 199 | " 1 %zd 1 %s\n", strlen(passwd), passwd); 200 | 201 | snprintf(cstore_cmd, sizeof(cstore_cmd), 202 | "bfd add %u %u %s %s %u %u %u %u %u%s", 203 | ifi, vrfid, src, dest, 204 | admin_down, clid, 205 | tx_des, rx_req, det_mult, 206 | auth); 207 | } 208 | 209 | if (del) 210 | snprintf(cstore_cmd, sizeof(cstore_cmd), 211 | "bfd del %u %u %s %s %u", 212 | ifi, vrfid, src, dest, admin_down); 213 | 214 | if (add || del) 215 | if (vplaned_cstore_store(cstore_key, cstore_cmd, NULL, 216 | add ? "SET" : "DELETE", 0) != 0) { 217 | printf("Failed to update session\n"); 218 | return EXIT_FAILURE; 219 | } 220 | 221 | if (monitor) { 222 | printf("Monitoring ...\n\n"); 223 | while (1) { 224 | zmsg_t *msg = zmsg_recv(sub); 225 | zframe_t *frame; 226 | uint32_t client; 227 | uint32_t state; 228 | char *event_type; 229 | 230 | if (zsys_interrupted) 231 | break; 232 | 233 | if (zmsg_size(msg) != 3) { 234 | printf("Incomplete message from dp\n"); 235 | break; 236 | } 237 | 238 | event_type = zmsg_popstr(msg); 239 | 240 | frame = zmsg_pop(msg); 241 | client = *(uint32_t *)zframe_data(frame); 242 | zframe_destroy(&frame); 243 | 244 | frame = zmsg_pop(msg); 245 | state = *(uint32_t *)zframe_data(frame); 246 | zframe_destroy(&frame); 247 | 248 | printf("%s Client %2x State %4s\n", event_type, client, 249 | bfd_state_name(state)); 250 | 251 | free(event_type); 252 | zmsg_destroy(&msg); 253 | } 254 | vplaned_event_disconnect(&sub); 255 | } 256 | 257 | return EXIT_SUCCESS; 258 | } 259 | -------------------------------------------------------------------------------- /test/example/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | executable( 5 | 'bfdtest', 6 | sources: ['bfdtest.c'], 7 | dependencies: [ 8 | b64_dep, 9 | czmq_dep, 10 | json_dep, 11 | vplaned_dep 12 | ], 13 | install: true, 14 | install_dir: '/opt/vyatta/bin' 15 | ) 16 | -------------------------------------------------------------------------------- /test/module/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-only 2 | # Copyright (c) 2020, AT&T Intellectual Property. 3 | 4 | cpputest_dep = dependency('cpputest') 5 | 6 | common_test_includes = include_directories( 7 | '../../daemon', 8 | 'src', 9 | 'src/mocks' 10 | ) 11 | common_test_sources = files( 12 | 'src/mocks/common_mocks.cpp', 13 | 'src/all_tests.cpp' 14 | ) 15 | 16 | hwbinding_test = executable( 17 | 'hwbinding_test', 18 | sources: [ 19 | '../../daemon/hwbinding.c', 20 | 'src/hwbinding_cpputest.cpp', 21 | common_test_sources 22 | ], 23 | include_directories: common_test_includes, 24 | dependencies: [ 25 | cpputest_dep, 26 | czmq_dep 27 | ] 28 | ) 29 | 30 | test('HW Binding', hwbinding_test, workdir : meson.current_source_dir()) 31 | 32 | lib_include = include_directories('../../lib') 33 | 34 | libvplaned_cfg_test = executable( 35 | 'libvplaned_cfg_test', 36 | sources: [ 37 | '../../lib/vplaned_cfg.c', 38 | 'src/libvplaned_cfg_cpputest.cpp', 39 | common_test_sources 40 | ], 41 | include_directories: [common_test_includes, lib_include], 42 | dependencies: [ 43 | cpputest_dep, 44 | czmq_dep, 45 | json_dep 46 | ] 47 | ) 48 | 49 | test('Vplaned CFG', libvplaned_cfg_test) 50 | 51 | libvplaned_cstore_test = executable( 52 | 'libvplaned_cstore_test', 53 | sources: [ 54 | '../../lib/vplaned_cstore.c', 55 | 'src/libvplaned_cstore_cpputest.cpp', 56 | dataplane_protobuf_c, 57 | controller_protobuf_c, 58 | common_test_sources 59 | ], 60 | include_directories: [common_test_includes, lib_include], 61 | dependencies: [ 62 | cpputest_dep, 63 | czmq_dep, 64 | json_dep, 65 | b64_dep, 66 | proto_c_dep 67 | ] 68 | ) 69 | 70 | test('Vplaned Config Store', libvplaned_cstore_test) 71 | 72 | nlmsg_test = executable( 73 | 'nlmsg_test', 74 | sources: [ 75 | '../../daemon/nlmsg.c', 76 | '../../daemon/topic.c', 77 | '../../daemon/mnlutil.c', 78 | 'src/nlmsg_cpputest.cpp', 79 | 'src/mocks/parser_mocks.cpp', 80 | common_test_sources 81 | ], 82 | include_directories: [common_test_includes], 83 | dependencies: [ 84 | cpputest_dep, 85 | czmq_dep, 86 | json_dep, 87 | mnl_dep, 88 | zmq_dep 89 | ], 90 | cpp_args : '-Wno-unused-parameter' 91 | ) 92 | 93 | test('Net Link MSG', nlmsg_test) 94 | 95 | parser_test = executable( 96 | 'parser_test', 97 | sources: [ 98 | '../../daemon/parser.c', 99 | 'src/parser_cpputest.cpp', 100 | common_test_sources 101 | ], 102 | include_directories: [common_test_includes], 103 | dependencies: [ 104 | cpputest_dep, 105 | czmq_dep, 106 | json_dep, 107 | ini_dep 108 | ], 109 | cpp_args : '-Wno-unused-parameter' 110 | ) 111 | 112 | test('Parser', parser_test, workdir : meson.current_source_dir()) 113 | 114 | request_test = executable( 115 | 'request_test', 116 | sources: [ 117 | '../../daemon/request.c', 118 | '../../daemon/vplane.c', 119 | '../../daemon/devname.c', 120 | 'src/request_cpputest.cpp', 121 | 'src/mocks/parser_mocks.cpp', 122 | 'src/mocks/snapshot_mocks.cpp', 123 | common_test_sources 124 | ], 125 | include_directories: [common_test_includes], 126 | dependencies: [ 127 | cpputest_dep, 128 | czmq_dep, 129 | json_dep, 130 | ], 131 | cpp_args : '-Wno-unused-parameter' 132 | ) 133 | 134 | test('Requests', request_test) 135 | 136 | snapshot_test = executable( 137 | 'snapshot_test', 138 | sources: [ 139 | '../../daemon/snapshot.c', 140 | '../../daemon/nlmsg.c', 141 | '../../daemon/topic.c', 142 | '../../daemon/mnlutil.c', 143 | 'src/snapshot_cpputest.cpp', 144 | 'src/mocks/parser_mocks.cpp', 145 | common_test_sources 146 | ], 147 | include_directories: [common_test_includes], 148 | dependencies: [ 149 | cpputest_dep, 150 | czmq_dep, 151 | json_dep, 152 | mnl_dep, 153 | zmq_dep 154 | ], 155 | cpp_args : '-Wno-unused-parameter' 156 | ) 157 | 158 | test('Requests', snapshot_test) 159 | 160 | vplane_test = executable( 161 | 'vplane_test', 162 | sources: [ 163 | '../../daemon/vplane.c', 164 | 'src/vplane_cpputest.cpp', 165 | 'src/mocks/parser_mocks.cpp', 166 | common_test_sources 167 | ], 168 | include_directories: [common_test_includes], 169 | dependencies: [ 170 | cpputest_dep, 171 | czmq_dep, 172 | json_dep, 173 | ], 174 | cpp_args : '-Wno-unused-parameter' 175 | ) 176 | 177 | test('Vplaned', vplane_test) -------------------------------------------------------------------------------- /test/module/src/all_tests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | */ 9 | 10 | #include "CppUTest/CommandLineTestRunner.h" 11 | 12 | int main(int ac, char **av) 13 | { 14 | MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); 15 | return CommandLineTestRunner::RunAllTests(ac, av); 16 | } 17 | -------------------------------------------------------------------------------- /test/module/src/hwbinding_cpputest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | */ 9 | 10 | #include "CppUTest/TestHarness.h" 11 | #include "CppUTestExt/MockSupport.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "cpputest_comparators.h" 18 | 19 | extern "C" { 20 | 21 | #include 22 | #include "controller.h" 23 | #include "CppUTestExt/MockSupport_c.h" 24 | 25 | } 26 | TEST_GROUP(hwbinding) 27 | { 28 | void teardown(void) { 29 | mock().checkExpectations(); 30 | mock().clear(); 31 | } 32 | }; 33 | 34 | TEST(hwbinding, interface_conf) 35 | { 36 | mock().expectNCalls(3, "logit") 37 | .withParameter("level", LOG_ERR); 38 | 39 | CHECK(get_name_by_pciaddr("0:0:1.0") == NULL); 40 | CHECK(get_name_by_pcislot(2, 0) == NULL); 41 | CHECK(get_name_by_mac("00:00:00:00:00:03") == 0); 42 | CHECK(get_name_by_port(5) == NULL); 43 | CHECK(get_name_by_fwidx(6) == NULL); 44 | 45 | read_interface_cfg("src/testcfgs/interface.conf"); 46 | 47 | CHECK(strcmp("s101", get_name_by_pciaddr("0:0:1.0")) == 0); 48 | CHECK(strcmp("s102", get_name_by_pcislot(2, 0)) == 0); 49 | CHECK(strcmp("s103", get_name_by_mac("00:00:00:00:00:03")) == 0); 50 | CHECK(strcmp("s104", get_name_by_port(4)) == 0); 51 | CHECK(strcmp("s105", get_name_by_fwidx(5)) == 0); 52 | CHECK(strcmp("s106", get_name_by_pcislot(6, 1)) == 0); 53 | CHECK(strcmp("s108", get_name_by_pciaddr("0:0:2.0")) == 0); 54 | 55 | CHECK(get_name_by_pciaddr(NULL) == NULL); 56 | CHECK(get_name_by_mac(NULL) == 0); 57 | 58 | CHECK(get_name_by_pciaddr("0:0:0.0") == NULL); 59 | CHECK(get_name_by_pcislot(0, 0) == NULL); 60 | CHECK(get_name_by_mac("00:00:00:00:00:00") == 0); 61 | CHECK(get_name_by_port(0) == NULL); 62 | CHECK(get_name_by_fwidx(0) == NULL); 63 | 64 | // Illegal interface name in configuration 65 | CHECK(get_name_by_pciaddr("0:0:6.0") == NULL); 66 | 67 | // Inline comment in configuration 68 | CHECK(strcmp("s999", get_name_by_pciaddr("0:0:7.0")) == 0); 69 | 70 | // New configuration 71 | read_interface_cfg("src/testcfgs/interface.conf2"); 72 | 73 | CHECK(strcmp("s201", get_name_by_pciaddr("0:0:1.0")) == 0); 74 | CHECK(strcmp("s202", get_name_by_pcislot(2, 0)) == 0); 75 | CHECK(strcmp("s203", get_name_by_mac("00:00:00:00:00:03")) == 0); 76 | CHECK(strcmp("s204", get_name_by_port(4)) == 0); 77 | CHECK(strcmp("s205", get_name_by_fwidx(5)) == 0); 78 | 79 | // Should be missing now 80 | CHECK(get_name_by_pciaddr("0:0:7.0") == NULL); 81 | 82 | // Read missing file 83 | read_interface_cfg("src/testcfgs/missing.conf"); 84 | CHECK(get_name_by_pciaddr("0:0:1.0") == NULL); 85 | } 86 | -------------------------------------------------------------------------------- /test/module/src/libvplaned_cfg_cpputest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, 2021 AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2016 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | */ 9 | 10 | #include "CppUTest/TestHarness.h" 11 | #include "CppUTestExt/MockSupport.h" 12 | 13 | #include 14 | #include "cpputest_comparators.h" 15 | #include "json.h" 16 | 17 | extern "C" { 18 | 19 | #include "vplaned.h" 20 | #include "CppUTestExt/MockSupport_c.h" 21 | 22 | } 23 | 24 | #define VPD_TIMEOUT 50 25 | 26 | static char vpd_sock_path[PATH_MAX]; 27 | static int vpd_sock_path_instance; 28 | static zsock_t *vpd_sock_server; 29 | 30 | static const char *vpd_config = 31 | "{\"controller\": {" 32 | "\"auth_enabled\": false," 33 | "\"cfg_publish_url\": \"tcp://10.252.1.254:*\"," 34 | "\"cfg_request_url\": \"tcp://10.252.1.254:4415\"," 35 | "\"ctladdr\": \"10.252.1.254\"," 36 | "\"ctladdr_valid\": true," 37 | "\"publish_url\": \"tcp://[::ffff:10.252.1.254]:49152\"," 38 | "\"request_url\": \"tcp://[::ffff:10.252.1.254]:4415\"," 39 | "\"timeout\": 120000" 40 | "}}"; 41 | 42 | static const char *vpd_dpconfig0 = "{\"dataplanes\": [{}]}"; 43 | 44 | static const char *vpd_dpconfig2 = 45 | "{\"dataplanes\": [{" 46 | "\"clocktick\": 4065," 47 | "\"connected\": false," 48 | "\"connects\": 3," 49 | "\"control\": \"tcp://[::ffff:10.252.1.2]:49153\"," 50 | "\"ctladdr\": \"10.252.1.2\"," 51 | "\"delpend\": false," 52 | "\"id\": 2," 53 | "\"interfaces\": []," 54 | "\"local\": false," 55 | "\"sessionid\": \"002D5CD8A2\"," 56 | "\"timeout\": 120000," 57 | "\"uuid\": \"01234567-89ab-0001-0002-cdef01234567\"" 58 | "},{" 59 | "\"clocktick\": 4888," 60 | "\"connected\": true," 61 | "\"connects\": 3," 62 | "\"control\": \"tcp://[::ffff:10.252.1.1]:49153\"," 63 | "\"ctladdr\": \"10.252.1.1\"," 64 | "\"delpend\": false," 65 | "\"id\": 1," 66 | "\"interfaces\": []," 67 | "\"local\": false," 68 | "\"sessionid\": \"002D5CD8A1\"," 69 | "\"timeout\": 120000," 70 | "\"uuid\": \"01234567-89ab-0001-0001-cdef01234567\"" 71 | "}]" 72 | "}"; 73 | 74 | static bool 75 | vpd_server(const char *request, const char *sts, const char *response) 76 | { 77 | static int waittime = (500 * ZMQ_POLL_MSEC); 78 | zmsg_t *msg; 79 | zframe_t *topic; 80 | bool result; 81 | 82 | zsock_set_rcvtimeo(vpd_sock_server, waittime); 83 | msg = zmsg_recv(vpd_sock_server); 84 | if (msg == NULL) 85 | return false; 86 | 87 | // zmsg_dump(msg); 88 | 89 | topic = zmsg_first(msg); 90 | result = (topic != NULL) && 91 | zframe_streq(topic, request); 92 | 93 | if (result && (response != NULL)) 94 | CHECK(zsock_send(vpd_sock_server, "ss", sts, response) == 0); 95 | 96 | zmsg_destroy(&msg); 97 | return result; 98 | } 99 | 100 | TEST_GROUP(libvplaned_cfg) 101 | { 102 | void setup(void) { 103 | mock().disable(); 104 | /* 105 | * There is a race condition between the close 106 | * (teardown) and the subsequent create such that on 107 | * occasions the create can fail. Rather than figure out 108 | * the race, just create a unique socket for each test 109 | * case. 110 | */ 111 | snprintf(vpd_sock_path, sizeof(vpd_sock_path), 112 | "ipc:///var/tmp/libvpd-config.%d", 113 | ++vpd_sock_path_instance); 114 | vpd_sock_server = zsock_new_rep(vpd_sock_path); 115 | CHECK(vpd_sock_server != NULL); 116 | mock().enable(); 117 | } 118 | 119 | void teardown(void) { 120 | mock().checkExpectations(); 121 | mock().clear(); 122 | mock().removeAllComparatorsAndCopiers(); 123 | mock().disable(); 124 | zsock_destroy(&vpd_sock_server); 125 | CHECK(vpd_sock_server == NULL); 126 | mock().enable(); 127 | } 128 | }; 129 | 130 | TEST(libvplaned_cfg, connect_disconnect) 131 | { 132 | zsock_t *s; 133 | 134 | /* 135 | * Attempting to connect to the "real thing" (vplaned) is not 136 | * going to work under test conditions... 137 | */ 138 | s = vplaned_connect(); 139 | CHECK(s == NULL); 140 | vplaned_disconnect(&s); 141 | CHECK(s == NULL); 142 | 143 | s = __vplaned_connect(vpd_sock_path); 144 | CHECK(s != NULL); 145 | CHECK(zsock_is(s)); 146 | vplaned_disconnect(&s); 147 | CHECK(s == NULL); 148 | } 149 | 150 | TEST(libvplaned_cfg, request_response) 151 | { 152 | zsock_t *s; 153 | char *resp; 154 | json_object *jobj; 155 | 156 | s = __vplaned_connect(vpd_sock_path); 157 | CHECK(s != NULL); 158 | 159 | CHECK(vplaned_request_config(s) == 0); 160 | CHECK(vpd_server("GETCONFIG", "OK", vpd_config)); 161 | CHECK(vplaned_response_get(s, VPD_TIMEOUT, &resp) == 0); 162 | 163 | jobj = json_tokener_parse(resp); 164 | CHECK(jobj != NULL); 165 | json_object_put(jobj); 166 | free(resp); 167 | 168 | CHECK(vplaned_request_dataplane(s) == 0); 169 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig2)); 170 | CHECK(vplaned_response_get(s, VPD_TIMEOUT, &resp) == 0); 171 | 172 | jobj = json_tokener_parse(resp); 173 | CHECK(jobj != NULL); 174 | json_object_put(jobj); 175 | free(resp); 176 | 177 | /* 178 | * Provoke & check various error conditions 179 | */ 180 | CHECK(vplaned_request_config(NULL) < 0); 181 | 182 | CHECK(vplaned_response_get(NULL, 0, &resp) < 0); 183 | CHECK(vplaned_response_get(s, 0, NULL) < 0); 184 | 185 | CHECK(vplaned_request_config(s) == 0); 186 | CHECK(vpd_server("GETCONFIG", "ERR", vpd_config)); 187 | CHECK(vplaned_response_get(s, VPD_TIMEOUT, &resp) < 0); 188 | 189 | /* 190 | * This must be the last test case. Since we don't issue a 191 | * response, the socket ends up in a failed state (REQ-REP 192 | * forces a strict send/receive ordering of messages). Rather 193 | * than play with the ZMQ_REQ_RELAXED socket option, run this 194 | * last so that the connection is subsequently deleted. 195 | */ 196 | CHECK(vplaned_request_config(s) == 0); 197 | CHECK(vpd_server("GETCONFIG", "OK", NULL)); 198 | CHECK(vplaned_response_get(s, VPD_TIMEOUT, &resp) < 0); 199 | 200 | vplaned_disconnect(&s); 201 | CHECK(s == NULL); 202 | } 203 | 204 | TEST(libvplaned_cfg, dataplane) 205 | { 206 | zsock_t *s; 207 | zlist_t *list = zlist_new(); 208 | struct vplaned_dataplane *dp; 209 | 210 | s = __vplaned_connect(vpd_sock_path); 211 | CHECK(vplaned_request_dataplane(s) == 0); 212 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig0)); 213 | 214 | CHECK(vplaned_dp_get_list(s, VPD_TIMEOUT, false, list) == 0); 215 | CHECK(zlist_size(list) == 0); 216 | zlist_purge(list); 217 | 218 | CHECK(vplaned_request_dataplane(s) == 0); 219 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig0)); 220 | CHECK(vplaned_dp_get_first(s, VPD_TIMEOUT, false, &dp) == 0); 221 | CHECK(dp == NULL); 222 | vplaned_dp_destroy(&dp); 223 | 224 | CHECK(vplaned_request_dataplane(s) == 0); 225 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig0)); 226 | CHECK(vplaned_dp_get(s, VPD_TIMEOUT, 99, &dp) == 0); 227 | CHECK(dp == NULL); 228 | vplaned_dp_destroy(&dp); 229 | 230 | vplaned_disconnect(&s); 231 | 232 | s = __vplaned_connect(vpd_sock_path); 233 | CHECK(vplaned_request_dataplane(s) == 0); 234 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig2)); 235 | 236 | CHECK(vplaned_dp_get_list(s, VPD_TIMEOUT, true, list) == 0); 237 | dp = (struct vplaned_dataplane *)zlist_pop(list); 238 | CHECK(dp != NULL); 239 | CHECK(zlist_size(list) == 0); 240 | CHECK(vplaned_dp_id(dp) == 1); 241 | vplaned_dp_destroy(&dp); 242 | zlist_purge(list); 243 | 244 | CHECK(vplaned_request_dataplane(s) == 0); 245 | CHECK(vpd_server("GETVPCONFIG", "OK", vpd_dpconfig2)); 246 | 247 | CHECK(vplaned_dp_get_first(s, VPD_TIMEOUT, true, &dp) == 0); 248 | CHECK(dp != NULL); 249 | CHECK(vplaned_dp_id(dp) == 1); 250 | CHECK(vplaned_dp_is_connected(dp)); 251 | CHECK(streq(vplaned_dp_console(dp), "tcp://[::ffff:10.252.1.1]:49153")); 252 | vplaned_dp_destroy(&dp); 253 | 254 | zlist_destroy(&list); 255 | vplaned_disconnect(&s); 256 | } 257 | -------------------------------------------------------------------------------- /test/module/src/libvplaned_cstore_cpputest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, 2021 AT&T Intellectual Property. 3 | * All rights reserved. 4 | * 5 | * SPDX-License-Identifier: LGPL-2.1-only 6 | * 7 | */ 8 | 9 | #include "CppUTest/TestHarness.h" 10 | #include "CppUTestExt/MockSupport.h" 11 | 12 | #include 13 | #include "cpputest_comparators.h" 14 | #include "json.h" 15 | 16 | extern "C" { 17 | 18 | #include "vplaned.h" 19 | #include "CppUTestExt/MockSupport_c.h" 20 | 21 | } 22 | 23 | #define CSTORE_TIMEOUT 100 24 | 25 | static char cstore_sock_path[PATH_MAX]; 26 | static int cstore_sock_path_instance; 27 | static zsock_t *cstore_sock_server; 28 | 29 | static const char *cstore_json1 = 30 | "{\"security\":{\"firewall\":{\"name\":{\"test1\":{\"default-action\":" 31 | "{\"__INTERFACE__\":\"ALL\"," 32 | "\"__SET__\":\"npf-cfg add fw:test1 10000 action=accept\"}" 33 | "}}}}}"; 34 | 35 | static bool 36 | jsonstr_object_equal(json_object *o1, json_object *o2) 37 | { 38 | if (json_object_get_type (o1) != json_object_get_type (o2)) 39 | return false; 40 | 41 | switch(json_object_get_type(o1)) { 42 | case json_type_boolean: 43 | return json_object_get_boolean(o1) == 44 | json_object_get_boolean(o2); 45 | 46 | case json_type_double: 47 | return json_object_get_double(o1) == 48 | json_object_get_double(o2); 49 | 50 | case json_type_int: 51 | return json_object_get_int64(o1) == 52 | json_object_get_int64(o2); 53 | 54 | case json_type_string: 55 | return strcmp(json_object_get_string(o1), 56 | json_object_get_string(o2)) == 0; 57 | 58 | case json_type_object: 59 | if (json_object_object_length(o1) != 60 | json_object_object_length(o2)) 61 | return false; 62 | 63 | json_object_iter jiter; 64 | json_object_object_foreachC(o1, jiter) { 65 | json_object *valo2; 66 | 67 | if (!json_object_object_get_ex(o2, jiter.key, &valo2)) 68 | return false; 69 | 70 | if (!jsonstr_object_equal(jiter.val, valo2)) 71 | return false; 72 | } 73 | return true; 74 | 75 | case json_type_array: 76 | size_t len, i; 77 | 78 | len = json_object_array_length(o1); 79 | if (len != json_object_array_length(o2)) 80 | return false; 81 | 82 | for (i = 0; i < len; i++) { 83 | if (!jsonstr_object_equal( 84 | json_object_array_get_idx(o1, i), 85 | json_object_array_get_idx(o2, i))) 86 | return false; 87 | } 88 | return true; 89 | 90 | case json_type_null: 91 | return true; 92 | }; 93 | 94 | return false; 95 | } 96 | 97 | static bool 98 | jsonstr_equal(const char *jstr1, const char *jstr2) 99 | { 100 | if (jstr1 == jstr2) 101 | return true; 102 | 103 | if (jstr1 == NULL) 104 | return (jstr2 == NULL); 105 | 106 | if (jstr2 == NULL) 107 | return false; 108 | 109 | json_object *jobj1 = json_tokener_parse(jstr1); 110 | json_object *jobj2 = json_tokener_parse(jstr2); 111 | 112 | return jsonstr_object_equal(jobj1, jobj2); 113 | } 114 | 115 | static bool 116 | cstore_server(const char *jsonexp, const char *sts) 117 | { 118 | static int waittime = (500 * ZMQ_POLL_MSEC); 119 | zmsg_t *msg; 120 | bool result; 121 | 122 | zsock_set_rcvtimeo(cstore_sock_server, waittime); 123 | msg = zmsg_recv(cstore_sock_server); 124 | if (msg == NULL) 125 | return false; 126 | 127 | // zmsg_dump(msg); 128 | 129 | if (jsonexp == NULL) 130 | result = true; 131 | else { 132 | char *jsonrcv = zmsg_popstr(msg); 133 | result = jsonstr_equal(jsonexp, jsonrcv); 134 | free(jsonrcv); 135 | } 136 | 137 | if (sts != NULL) 138 | CHECK(zsock_send(cstore_sock_server, "s", sts) == 0); 139 | 140 | zmsg_destroy(&msg); 141 | return result; 142 | } 143 | 144 | TEST_GROUP(libvplaned_cstore) 145 | { 146 | void setup(void) { 147 | mock().disable(); 148 | snprintf(cstore_sock_path, sizeof(cstore_sock_path), 149 | "ipc:///var/tmp/libvpd-cstore.%d", 150 | ++cstore_sock_path_instance); 151 | cstore_sock_server = zsock_new_rep(cstore_sock_path); 152 | CHECK(cstore_sock_server != NULL); 153 | mock().enable(); 154 | } 155 | 156 | void teardown(void) { 157 | mock().checkExpectations(); 158 | mock().clear(); 159 | mock().removeAllComparatorsAndCopiers(); 160 | mock().disable(); 161 | zsock_destroy(&cstore_sock_server); 162 | CHECK(cstore_sock_server == NULL); 163 | mock().enable(); 164 | } 165 | }; 166 | 167 | TEST(libvplaned_cstore, connect_disconnect) 168 | { 169 | zsock_t *s; 170 | 171 | /* 172 | * Attempting to connect to the "real thing" (vplaned) is not 173 | * going to work under test conditions... 174 | */ 175 | s = vplaned_cstore_connect(); 176 | CHECK(s == NULL); 177 | vplaned_cstore_disconnect(&s); 178 | CHECK(s == NULL); 179 | 180 | s = __vplaned_cstore_connect(cstore_sock_path); 181 | CHECK(s != NULL); 182 | CHECK(zsock_is(s)); 183 | vplaned_cstore_disconnect(&s); 184 | CHECK(s == NULL); 185 | } 186 | 187 | TEST(libvplaned_cstore, error) 188 | { 189 | zsock_t *s; 190 | 191 | s = __vplaned_cstore_connect(cstore_sock_path); 192 | CHECK(s != NULL); 193 | CHECK(vplaned_cstore_request(s, 194 | "fablive default", 195 | "fabric default liveness enabled 0 0 0", 196 | NULL, NULL) < 0); 197 | CHECK(vplaned_cstore_response(NULL, 0) < 0); 198 | vplaned_cstore_disconnect(&s); 199 | CHECK(s == NULL); 200 | 201 | s = __vplaned_cstore_connect(cstore_sock_path); 202 | CHECK(s != NULL); 203 | CHECK(vplaned_cstore_response(s, CSTORE_TIMEOUT) < 0); 204 | 205 | /* 206 | * This must be the last test case. Since we don't issue a 207 | * response, the socket ends up in a failed state (REQ-REP 208 | * forces a strict send/receive ordering of messages). Rather 209 | * than play with the ZMQ_REQ_RELAXED socket option, run this 210 | * last so that the connection is subsequently deleted. 211 | */ 212 | CHECK(vplaned_cstore_request(s, 213 | "fablive default", 214 | "fabric default liveness enabled 0 0 0", 215 | NULL, "SET") == 0); 216 | CHECK(cstore_server(NULL, NULL)); 217 | CHECK(vplaned_cstore_response(s, CSTORE_TIMEOUT) < 0); 218 | vplaned_cstore_disconnect(&s); 219 | CHECK(s == NULL); 220 | } 221 | 222 | TEST(libvplaned_cstore, asynccfg) 223 | { 224 | zsock_t *s; 225 | 226 | s = __vplaned_cstore_connect(cstore_sock_path); 227 | CHECK(s != NULL); 228 | CHECK(vplaned_cstore_request( 229 | s, 230 | "security firewall name test1 default-action", 231 | "npf-cfg add fw:test1 10000 action=accept", 232 | NULL, "SET") == 0); 233 | CHECK(cstore_server(cstore_json1, "OK")); 234 | CHECK(vplaned_cstore_response(s, CSTORE_TIMEOUT) == 0); 235 | vplaned_cstore_disconnect(&s); 236 | CHECK(s == NULL); 237 | } 238 | -------------------------------------------------------------------------------- /test/module/src/mocks/common_mocks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | 9 | #include "CppUTest/TestHarness.h" 10 | #include "CppUTestExt/MockSupport.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "cpputest_comparators.h" 19 | 20 | static bool 21 | logmessages = 22 | #if defined(LOGIT) 23 | 1; 24 | #else 25 | 0; 26 | #endif 27 | 28 | /* 29 | * Both "die" and "panic" are marked as "noreturn" functions. Thus we 30 | * need a way to signal the test function that a fatal error has 31 | * occurred without returning to the code under test. The easiest option 32 | * is to generate an exception and have the test code catch the 33 | * exception. 34 | */ 35 | 36 | extern "C" { 37 | 38 | #include "CppUTestExt/MockSupport_c.h" 39 | 40 | int debug; 41 | 42 | void __panic(const char *funcname, const char *format, ...) 43 | { 44 | CHECK(format != NULL); 45 | mock_c()->actualCall("__panic") 46 | ->withStringParameters("funcname", funcname); 47 | throw __func__; 48 | } 49 | 50 | void die(const char *format, ...) 51 | { 52 | if (logmessages) { 53 | va_list ap; 54 | char line[1024]; 55 | 56 | va_start(ap, format); 57 | vsnprintf(line, sizeof(line), format, ap); 58 | va_end(ap); 59 | fprintf(stderr, "LOGIT (die): %s\n", line); 60 | } 61 | 62 | CHECK(format != NULL); 63 | mock_c()->actualCall("die"); 64 | throw __func__; 65 | } 66 | 67 | void logit(int level, char c, const char *format, ...) 68 | { 69 | if (logmessages) { 70 | va_list ap; 71 | char line[1024]; 72 | 73 | va_start(ap, format); 74 | vsnprintf(line, sizeof(line), format, ap); 75 | va_end(ap); 76 | fprintf(stderr, "LOGIT (%c): %s\n", c, line); 77 | } 78 | 79 | CHECK(format != NULL); 80 | switch (level) { 81 | case LOG_ERR: 82 | case LOG_CRIT: 83 | case LOG_ALERT: 84 | case LOG_EMERG: 85 | mock_c()->actualCall("logit") 86 | ->withIntParameters("level", level); 87 | break; 88 | default: 89 | break; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /test/module/src/mocks/cpputest_comparators.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * CppUTest only knows how to compare certain data types. For the rest, 9 | * we define 'custom' comparators, which are used as follows: 10 | * 11 | * (a) In TEST_GROUP, declare a MyComparator object. This must be 12 | * outside any function, or will not be in scope when needed. Then 13 | * install it in setup(), and remove it in teardown(), eg: 14 | * 15 | * TEST_GROUP(cpputest_fw_commands) 16 | * { 17 | * MyStringComparator stringComparator; 18 | * 19 | * void setup(void) 20 | * { 21 | * mock().installComparator("string", stringComparator); 22 | * } 23 | * void teardown(void) 24 | * { 25 | * mock().removeAllComparatorsAndCopiers(); 26 | * } 27 | * }; 28 | * 29 | * (b) Use the comparator as follows in mocks. This example has a C++ 30 | * expect call, and a C-style actual call: 31 | * 32 | * mock().expectOneCall("npf_new_configuration"). 33 | * withParameter("nat", 0). 34 | * withParameter("global", 1). 35 | * withParameter("tables", 1). 36 | * withParameterOfType("string", "name", (void *)"bridge"). 37 | * withParameter("type", RTE_LOGTYPE_BRIDGE). 38 | * andReturnValue(&test_conf); 39 | * 40 | * return((npf_conf_t *)mock_c()->actualCall("npf_new_configuration") 41 | * ->withIntParameters("nat", nat) 42 | * ->withIntParameters("global", global) 43 | * ->withIntParameters("tables", tables) 44 | * ->withParameterOfType("string", "name", (char *)name) 45 | * ->withIntParameters("type", type) 46 | * ->returnValue().value.pointerValue); 47 | */ 48 | #include 49 | #include 50 | 51 | extern "C" { 52 | #include "ip_addr.h" 53 | } 54 | 55 | /* 56 | * Comparator for IP address ip_addr objects. 57 | * Note that the parent class signatures changed and added a const qualifier to 58 | * the arguments between 3.4 and 3.7. To ensure compatibility with both versions 59 | * we thus need to implement both. 60 | */ 61 | class AddrComparator:public MockNamedValueComparator 62 | { 63 | public: 64 | virtual bool isEqual(void *obj1, void *obj2) 65 | { 66 | struct ip_addr *ip1 = (struct ip_addr *)obj1; 67 | struct ip_addr *ip2 = (struct ip_addr *)obj2; 68 | 69 | if (ip1->af != ip2->af) 70 | return false; 71 | 72 | if (ip1->af == AF_INET) 73 | return 0 == memcmp(&ip1->ip.v4, 74 | &ip2->ip.v4, 75 | sizeof(ip1->ip.v4)); 76 | 77 | if (ip1->af == AF_INET6) 78 | return 0 == memcmp(&ip1->ip.v6, 79 | &ip2->ip.v6, 80 | sizeof(ip1->ip.v6)); 81 | 82 | return false; 83 | } 84 | virtual bool isEqual(const void *obj1, const void *obj2) 85 | { 86 | return isEqual((void *)obj1, (void *)obj2); 87 | } 88 | virtual SimpleString valueToString(void *obj) 89 | { 90 | struct ip_addr *addr = (struct ip_addr *)obj; 91 | char buf[INET6_ADDRSTRLEN]; 92 | 93 | return StringFrom(inet_ntop(addr->af, &(addr->ip), buf, 94 | sizeof(buf))); 95 | } 96 | virtual SimpleString valueToString(const void *obj) 97 | { 98 | return valueToString((void *)obj); 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /test/module/src/mocks/fabric_crypto_mocks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2017 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Unit test mocks (stubs) 9 | */ 10 | 11 | #include "CppUTest/TestHarness.h" 12 | #include "CppUTestExt/MockSupport.h" 13 | #include 14 | 15 | extern "C" { 16 | 17 | #include "stubs.h" 18 | #include "parser.h" 19 | #include "controller.h" 20 | #include 21 | 22 | int fab_crypto_init(zsock_t *socket) 23 | { 24 | CPPUTEST_STUB_RET_VAL(0); 25 | } 26 | 27 | void fab_crypto_exit() 28 | { 29 | CPPUTEST_STUB_RET; 30 | } 31 | 32 | void fab_crypto_timer() 33 | { 34 | CPPUTEST_STUB_RET; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /test/module/src/mocks/fabric_topo_mocks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2017 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Unit test mocks (stubs) 9 | */ 10 | 11 | #include "CppUTest/TestHarness.h" 12 | #include "CppUTestExt/MockSupport.h" 13 | #include "vplane.h" 14 | 15 | extern "C" { 16 | 17 | 18 | void fabric_topo_upaddr_add(uint16_t id, const char *upaddr) 19 | { 20 | 21 | } 22 | void fabric_topo_upaddr_delete(uint16_t id, const char *upaddr) 23 | { 24 | 25 | } 26 | void fabric_topo_purge_upaddr(const vplane_t *vp) 27 | { 28 | 29 | } 30 | 31 | void fabric_topo_notify_connect(const vplane_t *vp) 32 | { 33 | 34 | } 35 | 36 | void fabric_topo_notify_disconnect(const vplane_t *vp) 37 | { 38 | 39 | } 40 | 41 | void fabric_topo_encrypt(bool encrypt) 42 | { 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/module/src/mocks/parser_mocks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Unit test mocks (stubs) 9 | */ 10 | 11 | #include "CppUTest/TestHarness.h" 12 | #include "CppUTestExt/MockSupport.h" 13 | #include 14 | 15 | extern "C" { 16 | 17 | #include "stubs.h" 18 | #include "parser.h" 19 | 20 | const char *parser_endpoint_request(void) 21 | { 22 | return mock_c()->actualCall("parser_endpoint_request") 23 | ->returnValue().value.stringValue; 24 | } 25 | 26 | const char *parser_endpoint_request_bound(void) 27 | { 28 | return ""; 29 | } 30 | 31 | const char *parser_endpoint_publish_bound(void) 32 | { 33 | return ""; 34 | } 35 | 36 | void 37 | parser_set_endpoint_request_bound(char *url) 38 | { 39 | } 40 | 41 | int parser_controller_timeout(void) 42 | { 43 | return 0; 44 | } 45 | 46 | parse_result_t parse_atoi(uint32_t *i, const char *value) 47 | { 48 | *i = 0; 49 | return PARSE_OK; 50 | } 51 | 52 | static struct ip_addr laddr; 53 | 54 | const struct ip_addr *parser_local_addr(void) 55 | { 56 | laddr.af = AF_INET; 57 | laddr.ip.v4.s_addr = inet_addr("127.0.0.1"); 58 | return &laddr; 59 | } 60 | 61 | sa_family_t parser_local_af(void) 62 | { 63 | return AF_INET; 64 | } 65 | 66 | bool parser_authentication_enabled(void) 67 | { 68 | return false; 69 | } 70 | 71 | const char *parser_authentication_certificate(void) 72 | { 73 | CPPUTEST_STUB_RET_VAL(NULL); 74 | } 75 | 76 | const char *parser_authentication_path(void) 77 | { 78 | CPPUTEST_STUB_RET_VAL(NULL); 79 | } 80 | 81 | int parser_get_json_config(const char *, zmsg_t *, char **) 82 | { 83 | return 0; 84 | } 85 | 86 | bool 87 | parser_fabric_encrypt_enabled(void) 88 | { 89 | return false; 90 | } 91 | void 92 | parser_set_fabric_encrypt(bool encrypt) 93 | { 94 | } 95 | 96 | void 97 | parser_set_fabric_address(struct ip_addr *addr) 98 | { 99 | } 100 | 101 | void 102 | parser_delete_fabric_address(struct ip_addr *addr) 103 | { 104 | } 105 | 106 | const struct ip_addr * 107 | parser_fabric_addr(sa_family_t af) 108 | { 109 | CPPUTEST_STUB_RET_VAL(NULL); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /test/module/src/mocks/snapshot_mocks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | * 8 | * Unit test mocks (stubs) 9 | */ 10 | 11 | #include "CppUTest/TestHarness.h" 12 | #include "CppUTestExt/MockSupport.h" 13 | #include 14 | 15 | extern "C" { 16 | 17 | #include "stubs.h" 18 | #include "parser.h" 19 | #include "controller.h" 20 | 21 | snapshot_t *snapshot_new(void) 22 | { 23 | CPPUTEST_STUB_RET_VAL(NULL); 24 | } 25 | 26 | void snapshot_destroy(snapshot_t **snap) 27 | { 28 | CPPUTEST_STUB_RET; 29 | } 30 | 31 | void snapshot_send(snapshot_t *self, void *socket, zframe_t *to) 32 | { 33 | CPPUTEST_STUB_RET; 34 | } 35 | 36 | void snapshot_send_ifindex(snapshot_t *self, void *socket, zframe_t *to, 37 | int ifindex) 38 | { 39 | CPPUTEST_STUB_RET; 40 | } 41 | 42 | uint64_t snapshot_seqno(const snapshot_t *self) 43 | { 44 | CPPUTEST_STUB_RET_VAL(0); 45 | } 46 | 47 | int snapshot_update(snapshot_t *snap, nlmsg_t *nmsg) 48 | { 49 | CPPUTEST_STUB_RET_VAL(0); 50 | } 51 | 52 | int sd_notify(int env, const char *state) 53 | { 54 | CPPUTEST_STUB_RET_VAL(0); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /test/module/src/mocks/stubs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 3 | * Copyright (c) 2015 by Brocade Communications Systems, Inc. 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-only 7 | */ 8 | #if !defined(__stubs_h__) 9 | #define __stubs_h__ 10 | 11 | extern "C" { 12 | 13 | #include 14 | #include "CppUTestExt/MockSupport_c.h" 15 | 16 | #define CPPUTEST_STUB_RET_VAL(val) \ 17 | __extension__ \ 18 | ({ \ 19 | fprintf(stderr, "\n*** %s not yet implemented ***\n", __func__); \ 20 | assert(0); \ 21 | return val; \ 22 | }) 23 | 24 | #define CPPUTEST_STUB_RET \ 25 | __extension__ \ 26 | ({ \ 27 | fprintf(stderr, "\n*** %s not yet implemented ***\n", __func__); \ 28 | assert(0); \ 29 | return; \ 30 | }) 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /test/module/src/nlmsg_cpputest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, AT&T Intellectual Property. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-only 5 | * 6 | */ 7 | 8 | #include "CppUTest/TestHarness.h" 9 | #include "CppUTestExt/MockSupport.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #include "stubs.h" 15 | #include "cpputest_comparators.h" 16 | 17 | extern "C" { 18 | 19 | #include "controller.h" 20 | #include "CppUTestExt/MockSupport_c.h" 21 | 22 | struct mynlmsg { 23 | struct nlmsghdr *nlh; 24 | char *topic; 25 | uint32_t ifindex; 26 | }; 27 | 28 | void fsnotify_add_mpls_watchers(void) 29 | { 30 | CPPUTEST_STUB_RET; 31 | } 32 | 33 | void fsnotify_add_redirects_watchers(void) 34 | { 35 | CPPUTEST_STUB_RET; 36 | } 37 | 38 | static struct mynlmsg *mynlmsg_alloc(int nlmsg_type, uint32_t ifindex, int id) 39 | { 40 | struct mynlmsg *msg = (struct mynlmsg *)calloc(1, sizeof(*msg)); 41 | struct nlmsghdr *nlh; 42 | struct nlmsghdr _nlh; 43 | char buf[1024]; 44 | size_t sz; 45 | 46 | if (msg == NULL) 47 | return NULL; 48 | 49 | _nlh.nlmsg_type = nlmsg_type; 50 | snprintf(buf, sizeof(buf), "%s %d %d", 51 | nlmsg_type_name_rtnl(&_nlh), 52 | ifindex, id); 53 | msg->topic = strdup(buf); 54 | msg->ifindex = ifindex; 55 | snprintf(buf, sizeof(buf), "netlink data %s", msg->topic); 56 | 57 | sz = NLMSG_LENGTH(strlen(buf)) + 1; 58 | nlh = (struct nlmsghdr *)malloc(sz); 59 | memset(nlh, 0, sz); 60 | nlh->nlmsg_len = sz; 61 | nlh->nlmsg_type = nlmsg_type; 62 | memcpy(NLMSG_DATA(nlh), buf, strlen(buf)+1); 63 | msg->nlh = nlh; 64 | return msg; 65 | } 66 | 67 | static void mynlmsg_free(struct mynlmsg **msgp) 68 | { 69 | struct mynlmsg *msg = *msgp; 70 | 71 | *msgp = NULL; 72 | free(msg->topic); 73 | free(msg->nlh); 74 | msg->ifindex = -1; 75 | free(msg); 76 | } 77 | 78 | static nlmsg_t *find_nlmsg(const struct mynlmsg *msg, zlist_t *list) 79 | { 80 | nlmsg_t *nlmsg; 81 | 82 | for (nlmsg = (nlmsg_t *)zlist_first(list); 83 | nlmsg != NULL; 84 | nlmsg = (nlmsg_t *)zlist_next(list)) { 85 | if ((strcmp(msg->topic, nlmsg_key(nlmsg)) == 0) && 86 | (memcmp(msg->nlh, 87 | nlmsg_data(nlmsg), 88 | msg->nlh->nlmsg_len) == 0)) 89 | return nlmsg; 90 | } 91 | 92 | return NULL; 93 | } 94 | 95 | static zlist_t *propagate_list; 96 | 97 | void nl_propagate_nlmsg(nlmsg_t *nmsg) 98 | { 99 | CHECK(zlist_append(propagate_list, nmsg) == 0); 100 | } 101 | 102 | } 103 | 104 | static uint64_t msg_seqno; 105 | 106 | TEST_GROUP(nlmsg) 107 | { 108 | void setup(void) { 109 | mock().disable(); 110 | msg_seqno = 0; 111 | debug = 1; 112 | propagate_list = zlist_new(); 113 | nlmsg_setup(); 114 | mock().enable(); 115 | } 116 | 117 | void teardown(void) { 118 | mock().checkExpectations(); 119 | mock().clear(); 120 | mock().removeAllComparatorsAndCopiers(); 121 | mock().disable(); 122 | msg_seqno = 0; 123 | debug = 0; 124 | nlmsg_cleanup(); 125 | zlist_destroy(&propagate_list); 126 | mock().enable(); 127 | } 128 | }; 129 | 130 | TEST(nlmsg, create_delete) 131 | { 132 | const char *topic = "link 99 0"; 133 | const char *data = "netlink message data"; 134 | nlmsg_t *msg1; 135 | nlmsg_t *msg2; 136 | 137 | msg1 = nlmsg_new(topic, ++msg_seqno, data, strlen(data)+1); 138 | CHECK(msg1 != NULL); 139 | CHECK(nlmsg_seqno(msg1) == msg_seqno); 140 | CHECK(strcmp(nlmsg_key(msg1), topic) == 0); 141 | CHECK(strcmp((const char *)nlmsg_data(msg1), data) == 0); 142 | nlmsg_free(msg1); 143 | 144 | msg1 = nlmsg_new(topic, ++msg_seqno, data, strlen(data)+1); 145 | CHECK(msg1 != NULL); 146 | CHECK(nlmsg_seqno(msg1) == msg_seqno); 147 | 148 | msg2 = nlmsg_copy(msg1); 149 | CHECK(msg2 != NULL); 150 | CHECK(nlmsg_seqno(msg2) == msg_seqno); 151 | CHECK(strcmp(nlmsg_key(msg2), topic) == 0); 152 | CHECK(strcmp((const char *)nlmsg_data(msg2), data) == 0); 153 | 154 | nlmsg_free(msg1); 155 | CHECK(strcmp(nlmsg_key(msg2), topic) == 0); 156 | nlmsg_free(msg2); 157 | } 158 | 159 | TEST(nlmsg, ifindex) 160 | { 161 | uint32_t ifidxs[] = {0, 99, 10, 3, 1234567, 2, 1, 0}; 162 | uint32_t *ifindex; 163 | 164 | CHECK(zhashx_size(nlmsg_ifindex_hash()) == 0); 165 | 166 | CHECK(!nlmsg_ifindex_lookup(1)); 167 | nlmsg_ifindex_del(1); 168 | 169 | ifindex = &ifidxs[1]; 170 | while (*ifindex != 0) { 171 | char buf[32]; 172 | 173 | snprintf(buf, sizeof(buf), "port-%u", *ifindex); 174 | CHECK(nlmsg_ifindex_add(*ifindex, buf)); 175 | CHECK(!nlmsg_ifindex_add(*ifindex, buf)); 176 | ifindex++; 177 | } 178 | 179 | ifindex = &ifidxs[1]; 180 | while (*ifindex != 0) { 181 | CHECK(nlmsg_ifindex_lookup(*ifindex)); 182 | ifindex++; 183 | } 184 | 185 | ifindex--; 186 | while(*ifindex != 0) { 187 | nlmsg_ifindex_del(*ifindex); 188 | CHECK(!nlmsg_ifindex_lookup(*ifindex)); 189 | ifindex--; 190 | } 191 | 192 | CHECK(zhashx_size(nlmsg_ifindex_hash()) == 0); 193 | } 194 | 195 | TEST(nlmsg, pending) 196 | { 197 | zlist_t *msglist = zlist_new(); 198 | uint32_t ifindex1 = 10; 199 | struct mynlmsg *msg; 200 | size_t count = 4; 201 | 202 | zlist_append(msglist, 203 | mynlmsg_alloc(RTM_NEWNETCONF, ifindex1, 1)); 204 | zlist_append(msglist, 205 | mynlmsg_alloc(RTM_NEWNETCONF, ifindex1, 2)); 206 | zlist_append(msglist, 207 | mynlmsg_alloc(RTM_NEWADDR, ifindex1, 3)); 208 | zlist_append(msglist, 209 | mynlmsg_alloc(RTM_NEWADDR, ifindex1, 4)); 210 | 211 | CHECK(zlist_size(msglist) == count); 212 | 213 | /* 214 | * Add the netlink messages to the pending list - simulate the 215 | * arrival of NETCONF & ADDR messages before the associated 216 | * NEWLINK message. 217 | */ 218 | for (msg = (struct mynlmsg *)zlist_first(msglist); 219 | msg != NULL; 220 | msg = (struct mynlmsg *)zlist_next(msglist)) { 221 | nlmsg_pending_add(msg->topic, msg->nlh, msg->ifindex); 222 | } 223 | 224 | CHECK(zlist_size(nlmsg_pending_list()) == count); 225 | 226 | for (msg = (struct mynlmsg *)zlist_first(msglist); 227 | msg != NULL; 228 | msg = (struct mynlmsg *)zlist_next(msglist)) { 229 | nlmsg_t *nlmsg; 230 | 231 | nlmsg = find_nlmsg(msg, nlmsg_pending_list()); 232 | CHECK(nlmsg != NULL); 233 | } 234 | 235 | /* 236 | * Unexpected arrival of a DELLINK message, ensure the pending 237 | * messages are purged. 238 | */ 239 | nlmsg_ifindex_del(ifindex1); 240 | CHECK(zlist_size(nlmsg_pending_list()) == 0); 241 | 242 | for (msg = (struct mynlmsg *)zlist_first(msglist); 243 | msg != NULL; 244 | msg = (struct mynlmsg *)zlist_next(msglist)) { 245 | nlmsg_pending_add(msg->topic, msg->nlh, msg->ifindex); 246 | } 247 | 248 | /* 249 | * Simulate the arrival of the corresponding NEWLINK 250 | */ 251 | nlmsg_pending_propagate(ifindex1, &msg_seqno); 252 | CHECK(zlist_size(nlmsg_pending_list()) == 0); 253 | CHECK(zlist_size(propagate_list) == count); 254 | 255 | /* 256 | * Have all the messages been "published"? 257 | */ 258 | for (msg = (struct mynlmsg *)zlist_first(msglist); 259 | msg != NULL; 260 | msg = (struct mynlmsg *)zlist_next(msglist)) { 261 | nlmsg_t *nlmsg; 262 | 263 | nlmsg = find_nlmsg(msg, propagate_list); 264 | CHECK(nlmsg != NULL); 265 | zlist_remove(propagate_list, nlmsg); 266 | nlmsg_free(nlmsg); 267 | } 268 | 269 | CHECK(zlist_size(propagate_list) == 0); 270 | 271 | while ((msg = (struct mynlmsg *)zlist_pop(msglist)) != NULL) 272 | mynlmsg_free(&msg); 273 | 274 | CHECK(zlist_size(msglist) == 0); 275 | zlist_destroy(&msglist); 276 | } 277 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/2vplanes.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Controller Configuration 3 | # Auto-generated by controllercfg on Mon Jun 1 10:25:24 2015 4 | # 5 | [Controller] 6 | ip = 192.168.250.100 7 | publish = tcp://192.168.250.100:5903 8 | request = tcp://192.168.250.100:5904 9 | timeout = 10 10 | 11 | [Authentication] 12 | method = none 13 | path = /config/auth 14 | 15 | [Dataplane.fabric1] 16 | ip = 192.168.250.101 17 | uuid = 62FCE0B9-EE2D-8F41-9F3D-49A3E554F83D 18 | 19 | [Dataplane.fabric3] 20 | ip = 192.168.250.103 21 | uuid = D9A20081-BE15-BB43-99E6-62B810596F3B 22 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/2vplanes6.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Controller Configuration 3 | # Auto-generated by controllercfg on Mon Jun 1 10:25:24 2015 4 | # 5 | [Controller] 6 | ip = 2001::2 7 | publish = tcp://[2001::2]:5903 8 | request = tcp://[2001::2]:5904 9 | timeout = 10 10 | 11 | [Authentication] 12 | method = none 13 | path = /config/auth 14 | 15 | [Dataplane.fabric5] 16 | ip = 2001::1 17 | uuid = 62FCE0B9-EE2D-8F41-9F3D-49A3E554F835 18 | 19 | [Dataplane.fabric6] 20 | ip = 2001::3 21 | uuid = D9A20081-BE15-BB43-99E6-62B810596F36 22 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/bad.conf: -------------------------------------------------------------------------------- 1 | # Default controller configuration 2 | # Controller on same machine over loopback 3 | [Controller] 4 | ip=127.0.1 5 | ip= 6 | 7 | [Dataplane.fabric001] 8 | 9 | [Dataplane.fabric0xyz] 10 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/default.conf: -------------------------------------------------------------------------------- 1 | # Default controller configuration 2 | # Controller on same machine over loopback 3 | [Controller] 4 | ip=127.0.0.1 5 | interface=lo 6 | 7 | [Dataplane.fabric0] 8 | ip=127.0.0.1 9 | uuid=00000000-0000-0000-0000-000000000000 10 | interface=lo 11 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/defvplanes.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Controller Configuration 3 | # Auto-generated by controllercfg on Mon Jun 1 10:25:24 2015 4 | # 5 | [Controller] 6 | ip = 192.168.250.100 7 | timeout = 10 8 | 9 | [Authentication] 10 | method = none 11 | path = /config/auth 12 | 13 | [Dataplane.fabric1] 14 | ip = 192.168.250.101 15 | uuid = 62FCE0B9-EE2D-8F41-9F3D-49A3E554F83D 16 | 17 | [Dataplane.fabric3] 18 | ip = 192.168.250.103 19 | uuid = D9A20081-BE15-BB43-99E6-62B810596F3B 20 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/interface.conf: -------------------------------------------------------------------------------- 1 | # test interface.conf 2 | s101 pci-address 0000:00:01.0 3 | s102 pci-slot 2 4 | s103 mac 0:0:0:0:0:3 5 | s104 port 4 6 | s105 firmware-index 5 7 | s106 pci-slot 6.1 8 | 9 | s107 mac 0:0:0:0:0:g 10 | 11 | s108 pci-address 00:02.0 12 | 13 | 999 pci-address 0000:00:06.0 14 | 15 | s999 pci-address 0000:00:07.0 # comment 16 | 17 | thisnameistoolong firmware-index 42 18 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/interface.conf2: -------------------------------------------------------------------------------- 1 | # test interface.conf2 2 | s201 pci-address 0000:00:01.0 3 | s202 pci-slot 2 4 | s203 mac 00:00:00:00:00:03 5 | s204 port 4 6 | s205 firmware-index 5 7 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/local.conf: -------------------------------------------------------------------------------- 1 | # Default controller configuration 2 | # Controller on same machine over loopback 3 | [Controller] 4 | ip=127.0.0.1 5 | publish=ipc:///var/run/vyatta/vplaned.pub 6 | request=ipc:///var/run/vyatta/vplaned.req 7 | interface=lo 8 | timeout=86400 9 | 10 | [Dataplane.fabric0] 11 | ip=127.0.0.1 12 | uuid=00000000-0000-0000-0000-000000000000 13 | control=ipc:///var/run/vyatta/dataplane.control 14 | interface=lo 15 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/novplanes.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Controller Configuration 3 | # Auto-generated by controllercfg on Mon Jun 1 10:25:24 2015 4 | # 5 | [Controller] 6 | ip = 2001::2 7 | timeout = 10 8 | 9 | [Authentication] 10 | method = none 11 | path = /config/auth 12 | 13 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/routesourcebad.conf: -------------------------------------------------------------------------------- 1 | # Invalid route source 2 | [Controller] 3 | ip=127.0.0.1 4 | interface=lo 5 | route-source=invalid 6 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/routesourcekernel.conf: -------------------------------------------------------------------------------- 1 | # Use kernel as route source 2 | [Controller] 3 | ip=127.0.0.1 4 | interface=lo 5 | route-source=kernel 6 | 7 | [Dataplane.fabric0] 8 | ip=127.0.0.1 9 | uuid=00000000-0000-0000-0000-000000000000 10 | interface=lo 11 | -------------------------------------------------------------------------------- /test/module/src/testcfgs/routesourcerib.conf: -------------------------------------------------------------------------------- 1 | # Use RIB as route source 2 | [Controller] 3 | ip=127.0.0.1 4 | interface=lo 5 | route-source=rib 6 | 7 | [Dataplane.fabric0] 8 | ip=127.0.0.1 9 | uuid=00000000-0000-0000-0000-000000000000 10 | interface=lo 11 | -------------------------------------------------------------------------------- /test/resync.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Publisher standalone test 3 | * 4 | * To use: 5 | * testclient tcp://127.0.0.1:5569 tcp://127.0.0.1:5568 6 | * 7 | * Will ask for snapshot and then monitor for changes 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "publisher.h" 26 | 27 | int debug; 28 | 29 | static void usage(void) 30 | { 31 | fprintf(stderr, "Usage: nlserver URL interface...\n"); 32 | exit(1); 33 | } 34 | 35 | void __panic(const char *funcname, const char *fmt, ...) 36 | { 37 | va_list ap; 38 | const char *err = errno ? strerror(errno) : NULL; 39 | char line[1024]; 40 | 41 | va_start(ap, fmt); 42 | vsnprintf(line, sizeof(line), fmt, ap); 43 | va_end(ap); 44 | 45 | fprintf(stderr, "PANIC in %s()\n", funcname); 46 | if (err) 47 | fprintf(stderr, "%s: %s\n", line, err); 48 | else 49 | fprintf(stderr, "%s\n", line); 50 | exit(1); 51 | } 52 | 53 | int main(int argc, char *argv []) 54 | { 55 | char filter[128]; 56 | 57 | if (strcmp(argv[1], "-d") == 0) { 58 | ++debug; 59 | --argc, ++argv; 60 | } 61 | 62 | if (argc < 3) 63 | usage(); 64 | 65 | zsock_t *snapshot = zsock_new_dealer(*++argv); 66 | if (!snapshot) 67 | panic("zsock_new_dealer: %s", *argv); 68 | 69 | zsock_t *subscriber = zsock_new_sub(*++argv, ""); 70 | if (!subscriber) 71 | panic("zsock_new_sub %s", *argv); 72 | 73 | fprintf(stderr, "getting snapshot\n"); 74 | /* Get snapshot */ 75 | zstr_send(snapshot, "HITME"); 76 | uint64_t sequence = 0; 77 | while (1) { 78 | nlmsg_t *nmsg = nlmsg_recv(snapshot); 79 | if (!nmsg) 80 | break; 81 | 82 | sequence = nlmsg_seqno(nmsg); 83 | if (streq(nlmsg_key(nmsg), "end")) 84 | break; 85 | 86 | nlmsg_dump(NULL, nmsg); 87 | nlmsg_free(nmsg); 88 | } 89 | 90 | /* Listen for and decode netlink messages */ 91 | fprintf(stderr, "waiting for messages...\n"); 92 | while (!zctx_interrupted) { 93 | nlmsg_t *nmsg = nlmsg_recv(subscriber); 94 | if (!nmsg) 95 | break; 96 | 97 | uint64_t this = nlmsg_seqno(nmsg); 98 | if (this > sequence) { 99 | sequence = this; 100 | nlmsg_dump(NULL, nmsg); 101 | } else 102 | fprintf(stderr, "."); 103 | nlmsg_free(nmsg); 104 | } 105 | 106 | zsock_destroy(&snapshot); 107 | zsock_destroy(&subscriber); 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /test/tunnel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Basic standalone test 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | static const char *cfgfile = "/etc/vyatta/dataplane.conf"; 12 | static const char *local_addr = "127.0.0.1"; 13 | static const char *ether_addr = "00:11:22:33:44:55"; 14 | static uint32_t key = 0; 15 | static char *endpoint; 16 | static char *identity; 17 | 18 | static void die(const char *str) 19 | { 20 | perror(str); 21 | exit(1); 22 | } 23 | 24 | static void usage(void) 25 | { 26 | fprintf(stderr, "testclient [-i id] [-k key] [-f config] [-l local_ip]\n"); 27 | exit(1); 28 | } 29 | 30 | 31 | static void check(zsock_t *socket) 32 | { 33 | zmsg_t *msg = zmsg_recv(socket); 34 | 35 | if (!msg) 36 | die("zmsg_recv"); 37 | 38 | zmsg_dump(msg); 39 | char *answer = zmsg_popstr(msg); 40 | printf("I: %s\n", answer); 41 | 42 | if (!streq(answer, "OK")) { 43 | fprintf(stderr, "E: %s\n", answer); 44 | exit(1); 45 | } 46 | 47 | zmsg_destroy(&msg); 48 | } 49 | 50 | static int parse_entry(void *user, const char *section, 51 | const char *name, const char *value) 52 | { 53 | if (strcasecmp("controller", section) == 0) { 54 | if (streq("broker", name)) 55 | endpoint = strdup(value); 56 | } else if (strcasecmp("dataplane", section) == 0) { 57 | if (streq("uuid", name)) 58 | identity = strdup(value); 59 | } 60 | 61 | return 1; 62 | } 63 | 64 | /* Load config file and do sanity checks */ 65 | static void parse_cfg_file(void) 66 | { 67 | FILE *f = fopen(cfgfile, "r"); 68 | if (f == NULL) { 69 | perror(cfgfile); 70 | exit(EXIT_FAILURE); 71 | } 72 | 73 | int rc = ini_parse_file(f, parse_entry, NULL); 74 | if (rc) { 75 | fprintf(stderr, "Config file format error %s line %d\n", cfgfile, rc); 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | fclose(f); 80 | } 81 | 82 | int main(int argc, char **argv) 83 | { 84 | int opt; 85 | 86 | while ((opt = getopt(argc, argv, "f:l:e:k:")) != -1) 87 | switch(opt) { 88 | case 'f': cfgfile = optarg; break; 89 | case 'k': key = atoi(optarg); break; 90 | case 'l': local_addr = optarg; break; 91 | case 'e': ether_addr = optarg; break; 92 | default: 93 | usage(); 94 | } 95 | 96 | parse_cfg_file(); 97 | 98 | if (!identity) { 99 | fprintf(stderr, "can't find uuid\n"); 100 | exit(1); 101 | } 102 | 103 | in_addr_t laddr = inet_addr(local_addr); 104 | if (laddr == 0) { 105 | fprintf(stderr, "bad local address\n"); 106 | exit(1); 107 | } 108 | 109 | struct ether_addr *eth = ether_aton(ether_addr); 110 | if (!eth) { 111 | fprintf(stderr, "bad ether address\n"); 112 | exit(1); 113 | } 114 | 115 | zsock_t *client = zsock_new(ZMQ_REQ); 116 | if (!client) 117 | die("zsock_new"); 118 | 119 | zsock_set_identity(client, identity); 120 | 121 | printf ("I: connecting to server at %s...\n", endpoint); 122 | if (zsock_connect(client, endpoint) < 0) 123 | die("zsock_connect"); 124 | 125 | zmsg_t *msg = zmsg_new(); 126 | 127 | zmsg_addmem(msg, &key, sizeof(key)); 128 | zmsg_addstr(msg, "CREATE"); 129 | zmsg_addstr(msg, "p4p1"); 130 | zmsg_addmem(msg, &laddr, sizeof(laddr)); 131 | zmsg_addmem(msg, eth, sizeof(struct ether_addr)); 132 | 133 | printf("I: sending create\n"); 134 | zmsg_dump(msg); 135 | zmsg_send(&msg, client); 136 | 137 | check(client); 138 | 139 | msg = zmsg_new(); 140 | zmsg_addmem(msg, &key, sizeof(key)); 141 | zmsg_addstr(msg, "UP"); 142 | printf("I: sending up\n"); 143 | zmsg_dump(msg); 144 | zmsg_send(&msg, client); 145 | check(client); 146 | 147 | msg = zmsg_new(); 148 | zmsg_addmem(msg, &key, sizeof(key)); 149 | zmsg_addstr(msg, "DOWN"); 150 | printf("I: sending down\n"); 151 | zmsg_dump(msg); 152 | zmsg_send(&msg, client); 153 | check(client); 154 | 155 | zsock_destroy(&client); 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /tools/hwbinding: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | # Copyright (c) 2019, AT&T Intellectual Property. All rights reserved. 4 | # Copyright (c) 2016, Brocade Communications Systems, Inc. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-only 8 | 9 | # Scan "interfaces dataplane hardware" and create 10 | # /etc/vyatta/interfaces.conf. See the YANG definition for more 11 | # information about the possible configurations. 12 | # 13 | # This file is read by the vyatta-controller to force particular 14 | # dataplane interfaces to particular names. 15 | # 16 | # Each YANG hardware grouping is converted to a triplet in the 17 | # interfaces.conf file. 18 | # 19 | # 20 | # 21 | # An optional . or @ may be appended 22 | # to the hwbinding value (as allowed by the YANG model). 23 | 24 | use strict; 25 | use warnings; 26 | 27 | use File::Copy; 28 | use lib "/opt/vyatta/share/perl5/"; 29 | use XorpConfigParser; 30 | use Vyatta::Config::Migrate qw(find_nodes); 31 | 32 | my $interface_conf = '/etc/vyatta/interface.conf'; 33 | 34 | sub match_prefix { 35 | my ( $name, $prefix ) = @_; 36 | my @parts = split / /, $name; 37 | return $parts[0] eq $prefix; 38 | } 39 | 40 | # This gets complex it builds a list of matching trees 41 | # and returns the nodes 42 | sub lookup_path { 43 | my ( $xcp, $path ) = @_; 44 | my @components = split / /, $path; 45 | my $root = shift @components; 46 | my $trees = [ [$root] ]; 47 | return unless $xcp->get_node( $trees->[0] ); 48 | 49 | while ( my $elem = shift @components ) { 50 | my @matches; 51 | 52 | foreach my $t ( @{$trees} ) { 53 | my $head = $xcp->get_node($t); 54 | die "can't get [", join( ' ', @{$t} ), "]\n" 55 | unless defined($head); 56 | 57 | foreach my $child ( @{ $head->{children} } ) { 58 | 59 | # look for 'dataplane dp0p1p1' , 'dataplane' 60 | next unless ( match_prefix( $child->{name}, $elem ) ); 61 | 62 | my $subtree = [ @{$t}, $child->{name} ]; 63 | push @matches, $subtree; 64 | } 65 | } 66 | 67 | return unless @matches; 68 | $trees = \@matches; 69 | } 70 | 71 | my @nodes; 72 | foreach my $t ( @{$trees} ) { 73 | push @nodes, $xcp->get_node($t); 74 | } 75 | return @nodes; 76 | } 77 | 78 | my $f; 79 | 80 | my $config = shift; 81 | exit 1 unless $config; 82 | 83 | my $xcp = new XorpConfigParser(); 84 | $xcp->parse($config); 85 | 86 | open $f, '>', $interface_conf 87 | or die "can't open $interface_conf"; 88 | 89 | printf $f "# Automatically generated file; DO NOT EDIT.\n"; 90 | 91 | foreach my $intf ( lookup_path( $xcp, 'interfaces dataplane' ) ) { 92 | ( undef, my $ifname ) = split( ' ', $intf->{name} ); 93 | $ifname =~ s/^dp\d+//; 94 | 95 | foreach my $hardware ( find_nodes( $xcp, $intf, 'hardware' ) ) { 96 | my $dev_port; 97 | my $pci_function; 98 | my $hwbinding_type; 99 | my $hwbinding_value; 100 | 101 | foreach my $child ( @{ $hardware->{children} } ) { 102 | ( my $name, my $value ) = split( ' ', $child->{name} ); 103 | 104 | if ( $name eq "pci-function" ) { 105 | $pci_function = $value; 106 | } 107 | elsif ( $name eq "dev-port" ) { 108 | $dev_port = $value; 109 | } 110 | else { 111 | $hwbinding_type = $name; 112 | $hwbinding_value = $value; 113 | } 114 | } 115 | 116 | if ( defined $hwbinding_type ) { 117 | printf $f "%s\t%s\t%s", $ifname, $hwbinding_type, $hwbinding_value; 118 | if ( defined $pci_function ) { 119 | printf $f ".%d", $pci_function; 120 | } 121 | if ( defined $dev_port ) { 122 | printf $f "@%d", $dev_port; 123 | } 124 | printf $f "\n"; 125 | } 126 | 127 | } 128 | } 129 | 130 | close $f; 131 | 132 | exit 0; 133 | --------------------------------------------------------------------------------