Compare commits

..

189 Commits

Author SHA1 Message Date
Kostiantyn Buravchenko
fa325f283a Merge branch 'review-fixes' into 'larch_platform_github'
Review fixes for uCentral upstream contribution

See merge request openlan/ols-ucentral-client!39
2024-10-04 11:58:43 +00:00
Viacheslav Holovetskyi
ff381be8b5 Remove requirements from the "requires" and "after" manifest lists, as those ..
.. requirements are not crossplatform across different platforms
2024-10-02 15:53:21 +03:00
Viacheslav Holovetskyi
e3ae4b0324 Proper parsing of jumbo-frames option
Parse switch.jumbo-frames instead of switch.properties.jumbo-frames
(according to uCentral schema)
2024-10-02 15:37:58 +03:00
Viacheslav Holovetskyi
0a0820e169 Move declarations to top of the functions 2024-10-02 02:24:10 +03:00
Viacheslav Holovetskyi
2bc20dab60 Place curly brackets on the same line 2024-10-02 02:19:22 +03:00
Viacheslav Holovetskyi
22781547a1 Revert "Reimplement yang-to-gnmi path conversion .."
This reverts commit c0f92db209.
2024-10-02 02:08:45 +03:00
Viacheslav Holovetskyi
dc80301aee Revert "Disable JWT authentication"
This reverts commit 58d32190a8.
2024-10-02 02:08:39 +03:00
Viacheslav Holovetskyi
cfcbc2b724 Revert "Add debug symbols to the binaries, install gdb in Docker container"
This reverts commit 0d7a532f53.
2024-10-02 02:08:06 +03:00
Viacheslav Holovetskyi
ece8a6833f Adjust column limit to 120 2024-09-04 17:07:50 +03:00
Viacheslav Holovetskyi
1d3f947f89 Use spaces instead of tabs 2024-09-04 17:07:50 +03:00
Viacheslav Holovetskyi
98abb522eb Add clang-format config 2024-09-04 17:07:50 +03:00
Mykola Gerasymenko
1599ecceeb [igmp] Add IGMP-snooping support #29075 2024-09-04 17:07:50 +03:00
Viacheslav Holovetskyi
708b753f70 Implement jumbo frames configuration 2024-09-04 17:07:50 +03:00
Viacheslav Holovetskyi
c03231213d Implement jumbo frames config parsing 2024-09-04 17:07:50 +03:00
Dmitriy Nabok
7a9d2b19df Handle Syslog configuration #28496 2024-09-04 17:07:48 +03:00
Mykola Gerasymenko
7fc85d481c Fix comments review 2024-09-04 17:07:35 +03:00
Mykola Gerasymenko
5180f1e9da [stp] Add spanning-tree support #28505 2024-09-04 17:07:33 +03:00
Dmitriy Nabok
caef6ad4c3 refactoring 2024-09-04 17:06:54 +03:00
Dmitriy Nabok
57595fa5ae add dhcpv6 servers 2024-09-04 17:06:54 +03:00
Dmitriy Nabok
82cec0550a 28497: DHCP Relay init 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
8205ee7a0a Delete existing NTP servers that are not present in the config 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
43fd03ebb7 Implement NTP configuration via YANG models 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
6d46db300d Implement NTP config parsing 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
4d8ca0d9eb Fix includes 2024-09-04 17:06:54 +03:00
Andrii Mandiuk
fc1fecbb29 Add GNOI reboot 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
abee1977fc Disallow the configuration of VLAN 1
SONIC utilities doesn't allow to neither create VLAN 1, nor add any members
to it. gNMI lets you do this (for some reason), but in that case SWSS crashes,
and so do the whole system. Hence, we should also prevent it.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
bf1ca8fe89 Remove the services from "requires" list
This fields corresponds to the systemd's "Requires" clause, which means that
if one of these services gets stopped, ucentral-client services is stopped too,
which is generally not something we want
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f8c61adac1 Implement health reporting (always 100% for now) 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
1ac8d8cfa8 Implement metrics config as a JSON file instead of Protobuf
This is mostly done in order to be able to modify the config by hand
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2bc70cc145 Implement metrics config saving and loading 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f6a56a6a57 Add delay between retries, add additional debug messages 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
7b4947508d Add workaround for ucentral-client starting too early 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f6bfadad3e Add TIP issuing certificate to trusted CA certificates 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ddba3d5652 Bring the old RTTY client executable back 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
565a32fe74 Return nullopt in case if LLDP peer info is unavailable instead of throwing ..
.. an exception
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2c367ed818 Make get_state_info more fail-resistant
Retrieval failure of one particular part of state should not spoil the whole
telemetry response
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
3c9f3c56cf Add package dependencies to manifest 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
d4efe7e596 Revert "[To be reviewed] Implement serial retrieval from environment variable"
This reverts commit 209685905d2e0afcefc43252fba64e8c19808a20.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ea5edeaa60 Fix indentation 2024-09-04 17:06:54 +03:00
Kostiantyn Buravchenko
9f28fa135f Fixes to run in Sonic 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0667d9e4bd Don't include static routes and interface addresses into GW list 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ec69be8141 Implement dynamic routes 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
bd52b8b658 Implement static routes 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0bc7d0a373 Make error codes consistent 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
9a76bfadff Implement addition and deletion of interface addresses 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f8b293dbb8 Add interface addresses to platform state 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
b2b24f8e07 Implement getting interface addresses 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
1cc4e44a19 Add extern C to router-utils.h header 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2102f4e3c4 Add LAG interface mapping 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
3621e48411 Remove object ID prefix from the interface mapping keys 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
6080da5a05 Fix getting FDB entry MAC address 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
6a5f77f6fc Clear keys lists before scan to prevent processing them multiple times 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
bd31fdef3e Fix getting VLAN by object ID 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0aee7a3c74 Fix setting Redis SCAN cursor 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f1ffaedf0a Handle absence of counters, make it non-critical ..
.. (in this case all the counters are zeroes)
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
e53ccaf9b7 Handle the absence of oper-status field in interface YANG model 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
dffe061778 Add discrete exception type for gNMI errors 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
1b869b9d9d Add debug build flags for ucentral-client and larch-sonic platform 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
5aacf9a82d Implement FDB 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
7da9ce52d4 Link redis client library 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
4536be8e09 Fix port speed JSON type 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
3a8dd3666a Remove thread_local specifier from state variable ..
.. as the state is used from multiple threads and must be the same across them.
Maybe some synchronization will be required in future if concurrent
reads/writes would be possible.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c1d128a64b Implement saving and loading config id 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
7265aa1e70 Remove httplib dependency and related utilities as it's not used anymore 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
570b2ff1cc Implement plat_info_get using YANG model instead of a REST API 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
72fa3f794e Workaround for LLDP peer info 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
7120faa622 Use name instead of ifname as an interface name field 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
afda76f04d Implement LLDP peer info support 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c482e56767 Implement split_string utility 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0ad4d6e0ef Fix telemetry poll stop 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
38edc8b513 Implement telemetry and state polling 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c01e0a08ed Implement class for periodically called functions 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ce744a589b Use vector instead of heap-allocated array for ports info 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
3f14c324a0 Implement getting port info 2024-09-04 17:06:54 +03:00
Oleh
fc646e5daf Feature #28020. RTTY Support. Change name to Larch. 2024-09-04 17:06:54 +03:00
Oleh
d5291ec1cc Feature #28020 RTTY Support 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
9e2abdcd35 [To be reviewed] Implement serial retrieval from environment variable 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f4e37d67e5 [To be reviewed] Add CA certificate 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
54b15ebbc8 Fix port speed (should be integer instead of string) 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
11caedb29c Better handling of empty JSON responses ..
.. Some functions are expected to return non-default values, like
`get_vlan_membership()`, which must contain some amount of elements even if
the response is empty.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
9b6c77a6dc Fix errors if there are no ports or VLANs defined in the system ..
.. In this case JSON response is an completely empty object, that doesn't even
contains a, for example, "sonic-vlan:VLAN_LIST" object inside it.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
25b95decbb Fix processing of the last part of YANG path 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
7b20f06aef Implement key-value parsing for YANG paths 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
93360f91ac Fix port interface name field 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
6642f0b060 Remove unnecessary include, zero-initialize system info 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
5ffa368c60 Implement getting system info 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
af365a7479 Implement getting list and number of ports 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
f53624d25a Throw exceptions on JSON parse failures 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
74bcf8604a [To be reviewed] Fix multiple definitions of key variable ..
It's unclear whether this variable should be shared between compilation units
(definition in source file + `extern` in header) or should there be copy of
the variable in each compilation unit (simply make it `static`).

Currently this variable isn't used anywhere, so we can safely remove its
definition.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
39b58f3faf Add extern C to allow the use of log functions from C++ ..
Without `extern C` C++ will try to link against C++ (mangled) version of
`uc_log` function, which doesn't exist, hence the linking process fails with
an error.
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c7c038f04b Fix state value 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0137a3e87a Pass arguments by reference in gnmi_set 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
3c5cd07084 Implement config apply for ports 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2a96865f52 Add usings for frequently used std entities 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
69dab0a2fc Add logging, implement config applying, implement plat_reboot 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
22ed337904 Improve error-handling, perform refactoring 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
afd6e10a14 Fix port field instead of ifname as a port ID
For some reason it was `ifname` in Broadcom implementation, although this
field doesn't appear anywhere in SONIC YANG models
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
9f83e79568 Implement config apply for VLAN 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c88fa3515a Add missing header guards 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
fb4bdd5cf4 Download CMake manually as Debian-installed version doesn't suit the needs 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
78cc463fe9 Implement gnmi_operation class for batch updates and deletes 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
14073ecb83 Implement setting multiple entries via gNMI 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
cac8e861ab Restructure the project 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
af79b86980 Remove external libraries from the repo, download them with FetchContent instead 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
51dab835bc Implement set via gNMI 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
31cd55935f Implement get via gNMI 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ed64b97bf2 Implement dummy certificate verifier (connection doesn't succeed without it) 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
dda06db648 Move credentials shared_ptr instead of copying 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
4feb498fa0 Change REST API address to localhost 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
83f9155f4c Implement gRPC channel and gGNMI stub initialization 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
088f12dd89 Add the platform dependencies to the result file 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
99b0f6b797 Reorder targets to build by default 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
caf9199379 Add target to connect CMake to ucentral-client Makefile 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2a8e2338bf Implement CMake to build larch-sonic platform 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
2537609e91 Implement plat_info_get using sonic-restapi 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
40f3f78a72 Add empty implementation for Larch platform 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
ddd8b96c19 Install dev CA certificate in the Docker container 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
a4398e8588 Add Makefile target to build dpkg package for ARM64 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
88aed5461d Remove unnecessary mentions of ARM64 platform in build files ..
.. (It builds successfully without them)
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
c0f92db209 Reimplement yang-to-gnmi path conversion ..
.. Now it uses "origin" and "elem" fields, instead of a deprecated "element"
field
2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
58d32190a8 Disable JWT authentication 2024-09-04 17:06:54 +03:00
Viacheslav Holovetskyi
0d7a532f53 Add debug symbols to the binaries, install gdb in Docker container 2024-09-04 17:06:52 +03:00
Viacheslav Holovetskyi
9ba671834d Specify ARM64 architecture in build files 2024-09-04 15:43:35 +03:00
Olexandr, Mazur
5936fbed88 Merge pull request #13 from r4nx/fix-cpp-compilation
Fix compilation issue when platform is implemented in C++
2024-05-14 23:24:10 +03:00
Viacheslav Holovetskyi
0aea2e273c Fix compilation issues
new is a C++ keyword, so the header couldn't be used from C++
2024-04-29 16:17:53 +03:00
Olexandr, Mazur
6e8ccbf40c Merge pull request #12 from Telecominfraproject/plv_next/2.2_build5
Plv next/2.2 build5
2024-04-16 18:53:14 +03:00
Oleksandr Mazur
80f01f977c Update build version to 2.2 b5
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I8ba40d21d4f1d4c81eee906fce0bae6b76aa6052
2024-04-16 17:53:25 +03:00
Oleksandr Mazur
145f8aba82 Fixup scripts: make sure uplink iface (port) is dhcp trusted
With the DHCP-snooping full support for the BRCM platforms
it's needed for the ports to be marked either trusted/untrusted
to make sure proper DHCP request/reply forwarding occurs.

With this commit the following behavior is enforced:
 * Every port is untrusted by default upon device startup;
 * Uplink interface (port) is determined through the means of
   parsing ARP+FBD table;
 * DHCP trust is enabled only for the uplink port;
 * All vlan members can now send DHCP discover(s), which
   will be forwarded (flooded) to trusted ports (in our
   case to a single trusted uplink port) and get
   their replies back.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
2024-04-16 16:30:01 +03:00
Olexandr, Mazur
e53d618a33 Merge pull request #11 from Telecominfraproject/plv_next/2.2_build4
Plv next/2.2 build4
2024-04-08 14:39:14 +03:00
Oleksandr Mazur
0799cec723 Update build version to 2.2 b4
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: Id96cba2b6e1e53a0706133492172c17f860bb2f3
2024-04-08 14:18:08 +03:00
Oleksandr Mazur
24143fc5bc Fix infinite loop deviceupdate send
Deviceupdate condition never fails whenever password is changed,
thus makes the device spam with passwordchange events.
This, as a results, overflows internal buffers, which
overflows internal disk storage.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
2024-04-08 13:56:11 +03:00
Oleksandr Mazur
15b9868322 Revert "script: Use a new GW address"
This reverts commit 4911cab05e.
2024-04-08 13:37:10 +03:00
Olexandr, Mazur
54141e0af6 Merge pull request #10 from Telecominfraproject/plv_next/2.2_build3
Plv next/2.2 build3
2024-04-07 12:30:13 +03:00
Oleksandr Mazur
9034123c2e Update build version to 2.2 b3
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I2d6837667e39be364e32a4a73932928b1edd6b0a
2024-04-07 12:28:45 +03:00
Serhiy Boiko
d0189eaad6 plat: ipv4: Skip L3 configuration for default vlan
Do not apply or modify any L3 cfg for Vlan 1 (ip, dhcp, igmp, etc.)

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I0e19bad017cacc14cb30af946101d7ad4d9bc8d0
2024-04-07 12:28:38 +03:00
Serhiy Boiko
c349f3f9a4 proto: log: Remove unused code
'log' field is expected to be (and is defined in the schema as) an object,
so there is no need to handle it as an array.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I05dd8de1fad086fd6ea16a4cb4792ad7f6e826fd
2024-04-07 12:28:27 +03:00
Serhiy Boiko
5832ecdf36 cfg: Align config samples with current schema definitions
Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I7b4a2974d19ea645c7cfe7ab9ca1bde27a849f73
2024-04-07 12:28:19 +03:00
Serhiy Boiko
4911cab05e script: Use a new GW address
Use docker env variables to pass a new gw addr, since the old
gw (that is reported by the redirector) is not available.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ibb3a3b95d556996617d7f4ce1d6a58303df76ad7
2024-04-07 12:28:09 +03:00
Serhiy Boiko
7442bb79c3 ucentral-client: Fix env variable for GW address
Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I811f4faeb438d84e6cbd88905c9bb5846264ef5a
2024-04-07 12:27:58 +03:00
Serhiy Boiko
f972987312 proto: Fix RPVSTP config being rejected
'priority' should be a multiple of 4096

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ie974257c47be54852d8a634022ad6f033f06597a
2024-04-07 12:27:48 +03:00
Olexandr, Mazur
783368dd7b Merge pull request #9 from Telecominfraproject/feat/igmp_global_querier_filtering
plat: Parse new fields from config message
2024-04-02 13:00:06 +03:00
Serhiy Boiko
dc60bab84b plat: Parse new fields from config message
Parse and handle unknown-multicast-flood-control and querier-enable fields.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ifa0620bc22e3235b8fb4eb2f7f5dcd026ad0404f
2024-04-02 12:46:39 +03:00
Olexandr, Mazur
681efcabfc Merge pull request #8 from Telecominfraproject/plv_next/next_290324
Plv next/next 290324
2024-03-29 17:34:25 +02:00
Oleksandr Mazur
6f6bd4dfd0 Update build version to 2.2 b2
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I1091f1f34d38f197bdc272adab018a2ec68b5bbf
2024-03-29 17:31:04 +02:00
Serhiy Boiko
04e80e1650 proto: Move diagnostics to a separate thread
Running diagnostics takes a long time (up to 10mins). Running
it in the same context as the callback broker means that all
messages are blocked until diagnostics is finished.
Creating a new thread resolves this issue.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Iadb628007903a7d643b6d2e705da84fd04e73dbe
2024-03-29 17:27:58 +02:00
Serhiy Boiko
a8e2b18733 plat: Report uplink address in state message
Reported info:
- uplink ip addr
- mac addr
- egress port
- route metrics

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ifdae793be73f4c43b3daffb4cf3f4016ea989d44
2024-03-29 17:27:53 +02:00
Oleksandr Mazur
2e5499c375 Fix DHCP + NTP not working properly for Vlan1
Enable properly DHCP snooping for Vlan1 by default;
Make sure NTP is configured to use Vlan1 as wel;

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: Ided132b12a5d472954458632cd61b3e43e072fa0
2024-03-29 17:27:48 +02:00
Serhiy Boiko
0f64807cfb port-isolation: Update parser based on schema
Since port isolation schema moved from ethernet to switch update
the code acordingly.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I30335af3d17ad6ecc910c5c8ed2ca69eaaae0913
2024-03-29 17:27:41 +02:00
Serhiy Boiko
215d4dab4a vlan: Add SVI ip addrs to state message
Add netlink calls to get vlan ip addrs.
Add gnma api to retreive list of addrs.
Add "addresses" field to vlan interfaces.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I4a43485d45c75993ef128c952acfd69f04cd975e
2024-03-29 17:27:35 +02:00
Olexandr, Mazur
e13a8fac52 Merge pull request #7 from Telecominfraproject/plv_next/next_040324
Plv next/next 040324
2024-03-04 16:53:19 +02:00
Serhiy Boiko
049fef08d9 ipv4: Fix interface ipv4 cfg parsing
The schema requires the config to have the following format:
  {
    "ipv4": {
      "subnet": [
        { "prefix": "255.255.225.255/32" }
      ]
    }
  }

But the code expected this:
  {
    "ipv4": {
      "subnet": "255.255.225.255/32"
    }
  }

Since parsing ipv4 for vlans and ports is the same the code
is moved to a function.
*_interface_parse function were refactored to remove unnecessary
indentations.

Limitations:
Only a single ip address can be configured on an interface.
IP address will be configured only for the first port in the list.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ie3ed777a963129269b10833c970dc3e8a24b6b38
2024-03-04 16:50:18 +02:00
Serhiy Boiko
2bd145e09f cfg: Beautify json configs
Use https://codebeautify.org/jsonviewer to beautify all configs.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I4e82ae96927d4f63027c68f17d2179adb9f09052
2024-03-04 16:50:18 +02:00
Serhiy Boiko
732b4e1bc7 cfg: Add sample configs for new and old features
Sample configs added:
- log service
- port isolation
- igmp
- stp

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ia58fab2da04658100b5be1044892f4966d935e10
2024-03-04 16:50:18 +02:00
Serhiy Boiko
559776ba06 proto: Fix parser issue
WA for issue where an array of objects might have malformed keys:

Original message:
    {
        "static-mcast-groups": [
            {"address": "1.1.1.1",
             "egress-ports": [...]}
        ]
    }

Malformed message:
    {
        "static-mcast-groups": [
            {"static-mcast-groups[].address": "1.1.1.1",
             "static-mcast-groups[].egress-ports": [...]}
        ]
    }

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Id0ebe93ab976338adab6cdc3b7d6691ecca9dc94
2024-03-04 16:50:18 +02:00
tip-admin
b6c03319d3 Create LICENSE 2024-02-29 08:52:04 -08:00
Olexandr, Mazur
05d06592cc Merge pull request #5 from Telecominfraproject/plv_next_270224
Plv next 270224
2024-02-27 14:35:52 +02:00
Serhiy Boiko
ebf160fa06 igmp: Fix invalid port name size
sizeof(*port_node->name) == 1
sizeof(port_node->name) == PORT_MAX_NAME_LEN  # 32

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I3bd09eaf00bb55045a935de7795509f5582b0878
2024-02-27 14:04:18 +02:00
Serhiy Boiko
ace64ef341 stp: Fix STP config not applying
Fix issue where STP config was rejected on device because
some of the GNMI entries could not be deleted.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ida30be22c609c682b526358c98225bff0567290c
2024-02-27 14:04:16 +02:00
Serhiy Boiko
5acd35237c igmp: Change what attributes are set for snooping/querrier
Set querrier attributes only for `ip igmp ...`.
Set snooping attributes only for `ip igmp snooping ...`

fixes: a15c56f
Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I56369d323fdd8b2b63605392cb8b23fa0442bb8a
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
d9fae8097b Update build number 1.6 b5
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I63b7b8b9945ed67f6ddba104ec3c154c63a308dc
2024-02-27 14:04:12 +02:00
Serhiy Boiko
ee4ff0ee3a igmp: Configure igmp snooping and static groups
Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I679807532d04338077cc8657ed9702d3ad09536e
2024-02-27 14:04:12 +02:00
Serhiy Boiko
6efdcb7eb5 igmp: plat: Fill vlan interface data
Store IGMP info inside plat struct (GNMI/GNMA handlers);
Add vlan interfaces to the list of all interfaces (proto handlers).
Vlan interfaces have a multicast field as per state schema.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Id9778a017e0ba54f8e1154e580304f95e3de41c8
2024-02-27 14:04:12 +02:00
Serhiy Boiko
bca8160f67 sfp: Send transceiver info to GW
Notify GW about the ports' transceiver info. If transceiver
info is not supported the "transceiver-info" field is omitted.

    "interfaces": [
        ...
        {
            ...
            "transceiver-info": {
                "vendor-name": "VENDOR",
                "part-number": "PART NUMBER",
                "serial-number": "SERIAL",
                "revision": "REVISION",
                "temperature": 0,
                "tx-optical-power": 0,
                "rx-optical-power": 0,
                "max-module-power": 0,
                "form-factor": "FORM FACTOR",
                "supported-link-modes": [ ... ]}
            ...
        },
        ...
    ],

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: If83bb41d1ebf76b41c2ed0be6f3f755eefa18d29
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
3d2b3295e7 proto: fill state with CoA-related global counters
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I0979c0abf180fe5a8309fc43ed233352edda936c
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
15e4f7a580 gnma: Implement gnmi handlers for fetching COA stats
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: If5027dbb59522731312941b4e539f38e8a54dc70
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
1afbc126fe gnma: implement DAS + DAC gnmi handlers + plat_state recovery
Implement handlers for retrieving platstate of DAS configuration,
as well as DAC list (same as for RADIUS clients).

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: Ifa149aa1708c114cc4b5b59772d524f4cd5b70b2
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
a2c49e8ab5 gnma: remove unused radius host definition
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I3cb4739bd6f0724dad784caf381e9f7f01797153
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
2a8d2c18ce gnma: fix invalid poe stats reported
Newest BRCM images changed from number->string values of
some PoE-related info.
Fix parsing to report valid data back to cloud.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I46afee603f16439fa23adda698be748bfe008b04
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
4549ef61c3 proto: fix FSM for plat diagnostics
GW expects for reply to be sent immediatly (pending state).
This gives GW understanding that command's been processed,
but is still executing.
The final result would be sent afterwards, upon diag completion.
However, the initial pending should be sent as fast as possible,
upon parsing the command itself.

This change fixes the expectations of GW. Without this change
command is marked as timedout, then pending and the completed
OK.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I36925783fc2bc1cd7dfebb957d7ba30d2c7650ea
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
5710119746 proto: remove iface-type from port isolation
Schema changes pruned the type from port-isolation definition.
Aling basecode with schema requirements.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I985022fdefda25461734e8158df7464220f84d8b
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
cedf998260 gnma/proto: fix issues introduced with port-isolation support
- crash upon clearing port-isolation
- memleak of never-freed port-isolation cfg

Change-Id: I847708249cf85f2cfd40ebffefbd56cee822ea8d
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
963120f2b4 proto: implement port-isolation pasring
Only partial - json parsing - implementation is present.
Parsed (from JSON) config values can be used
in Platform code, upon applying conf, to alter port-isolation cfg.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I5deb31be5b5b2295a7698ca357fc10555b5dd772
2024-02-27 14:04:12 +02:00
Oleksandr Mazur
8b4a63fb66 proto: implement services (ssh, http, telnet) pasring
Implement partial (only enable/disable) parsing of services - common part
of OLS-NOS repo.
Only partial - json parsing - implementation is present.
Parsed (from JSON) config values (bool - enable true/false) can be used
in Platform code, upon applying conf, to alter services state.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I2c25724913b5524729950513fd6c5f3b1c25f9e0
2024-02-27 14:04:12 +02:00
Olexandr, Mazur
60abb9a7e6 Merge pull request #1 from Telecominfraproject/plv_next
Plv next
2024-01-22 20:29:41 +02:00
Serhiy Boiko
0b683379b4 system-password: Allow to change admin pass from GW
System (admin) password is changed every time the configure
message contains a system-password field:

  {
    ...
    "unit": {
      "system-password": "YourPaSsWoRd"
    }
    ...
  }

Every time the password is updated a deviceupdate message
(with the new password) is sent to the GW.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I9c8eb49a62402807d9de61e8020637da57986e52
2024-01-22 17:36:01 +02:00
Serhiy Boiko
a328cd6b7a mac-address-list: Add overflow flag
The flag is set in two cases:
- if the value of wired-clients-max-num is set to 0
- if the number of learned mac addrs is greater than
  the value of wired-clients-max-num

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I73b46dbb213a91f6375ec33106e84fed50d30ac6
2024-01-22 17:32:06 +02:00
Serhiy Boiko
7afff76db1 proto: Fill platform state with learned mac addresses
JSON message to GW:

    "mac-forwarding-table": {
        "Ethernet0": {
            "100": [ "11:11:11:11:11:11", "22:22:22:22:22:22" ],
            "200": [ "33:33:33:33:33:33", "44:44:44:44:44:44" ]
        },
        "Ethernet1": {
            "100": [ "55:55:55:55:55:55", "66:66:66:66:66:66" ],
            "200": [ "77:77:77:77:77:77", "88:88:88:88:88:88" ]
        }
    }

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I49a4380225bc105a880731df367df3efbd0f4908
2024-01-22 17:32:06 +02:00
Oleksandr Mazur
0d9af851b4 Update build number 1.6 b4
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I2df8401d9437179e9564b5cae3aa34428f1ce5c1
2024-01-22 17:32:06 +02:00
Serhiy Boiko
b8c952cf1c plat: Add learned_mac_addrs_get API
The API will store the list of learned MAC addrs inside
of plat_state_info.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ib1e03c3fbc9f52ee9037e6bfca1c2c8fb2db56df
2024-01-22 17:32:06 +02:00
Serhiy Boiko
8636487247 gnma: Add mac_addr_list_get API
This API will return a list of fdb entries from the device.
The caller is responsible of providing a big enough buffer.
If the buffer is not big enough then a GNMA_ERR_OVERFLOW
error is returned and the list_size arg is set to the minimum
required size.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I8046549d9aff5903a068ea3ea2914dcb154a34da
2024-01-22 17:32:06 +02:00
Serhiy Boiko
ac20c4c276 Refactor router utils
Make for_router_db_diff macro more readible.
Use int instead of bool in _fib_info_cmp.
This also fixes the case where unreachable routes were not
rejected by the device if they were replacing blackhole
routes with the same prefix.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ic560ce85e506715509437de765de535172ccbf67
2024-01-22 17:32:06 +02:00
Oleksandr Mazur
ee4b0ca66b Update build number 1.6 b3
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I9aaa730610dd3f107e146be72f113964447d72d9
2024-01-22 17:31:27 +02:00
Oleksandr Mazur
977c651079 Add unavailable reboot-case handler
Whenever system is not yet ready to determine reboot-cause
send an appropriate unavailable reboot cause msg, do not
default to <crash>.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
Change-Id: I33130375889ccf689f4e5bcc4e6ee0b5ceb6f76e
2024-01-22 17:31:27 +02:00
Yevhen Orlov
3beb5f314b Update build number 1.6 b2
Signed-off-by: Yevhen Orlov <yevhen.orlov@plvision.eu>

Change-Id: If2c54a2c44dbe90c615dcb15f4c9fb285eb7a364
2024-01-22 17:31:27 +02:00
Serhiy Boiko
a84bfa8e04 Fix segfault caused by blackhole routes
The for loop incorrectly handles the array of routes which causes
a NULL pointer dereference in case that we have any routes
configured at device init (e.g. blackhole).
If we don't have any routes at init time the loop is skipped and
the issue is not reproduced.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I5f3656c470141fbfad3ceb268d91f755e33cf65f
2024-01-22 17:31:27 +02:00
Yevhen Orlov
289c74a81d Add parameters overriding in order to be sure after upgrade
Change-Id: I43b25ea6717403dfb871c67549bc248fd22f09b9
2024-01-22 17:31:27 +02:00
Yevhen Orlov
be1138ebc6 Revert "Add MGMT_VRF config to config_db.json"
This reverts commit d85cd586a8bedef86f4793befea42b6e511d2254.

Decide to do so, because we has multiple issues regarding that some
services startted especially for mgmt vrf so unavalible in case that
eth0 is not used in our scenarios.

Change-Id: I5fd53edba7d54acc61efa624ea798c169775a3ad
2024-01-22 17:31:27 +02:00
Serhiy Boiko
764e9f93ab Update revision naming format
Introduce a define (PLATFORM_REVISION) that can be used to
represent the current platform revision.
The new format is: "Rel %s build %s"
PLATFORM_REVISION can be passed to the make cmd as an env variable.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: Ifab6df704946fe283a102b1985afe9cedc39eba7
2024-01-22 17:31:27 +02:00
Serhiy Boiko
12117ebfc8 Fix Spanning Tree configuration
Set parameters per vlan instead of globally.

Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu>
Change-Id: I011db1d4a800d0dc8463932fe692c4b57f898a55
2024-01-22 17:28:59 +02:00
108 changed files with 9548 additions and 3563 deletions

View File

@@ -1,4 +1,4 @@
FROM debian:buster
FROM arm64v8/debian:buster
LABEL Description="Ucentral client (Build) environment"
ARG HOME /root
@@ -15,13 +15,16 @@ RUN apt-get update -q -y && apt-get -q -y --no-install-recommends install \
libcurl4-openssl-dev \
libev-dev \
libssl-dev \
libnl-route-3-dev \
libnl-3-dev \
apt-utils \
git \
wget \
autoconf \
libtool \
pkg-config \
libjsoncpp-dev
libjsoncpp-dev \
libhiredis-dev
RUN git config --global http.sslverify false
RUN git clone https://github.com/DaveGamble/cJSON.git ${HOME}/ucentral-external-libs/cJSON/

28
LICENSE Normal file
View File

@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2024, Telecom Infra Project
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
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.
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.
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.

View File

@@ -7,8 +7,10 @@ IMG_ID := "ucentral-client-build-env"
IMG_TAG := $(shell cat Dockerfile | sha1sum | awk '{print substr($$1,0,11);}')
CONTAINER_NAME := "ucentral_client_build_env"
.PHONY: all clean build-host-env build-final-deb build-ucentral-docker-img run-host-env run-ucentral-docker-img \
plat-ec plat-ec-clean
DPKG_IMAGE := "dpkg-builder:latest"
DPKG_CONTAINER_NAME := "dpkg_build_env"
.PHONY: all clean build-host-env build-final-deb build-ucentral-docker-img run-host-env run-ucentral-docker-img
all: build-host-env build-ucentral-app build-ucentral-docker-img build-final-deb
@@ -33,6 +35,7 @@ run-host-env: build-host-env
docker run -d -t --name ${CONTAINER_NAME} \
-v $(realpath ./):/root/ols-nos \
--env UCENTRAL_PLATFORM=$(UCENTRAL_PLATFORM) \
--env PLATFORM_REVISION="$(PLATFORM_REVISION)" \
${IMG_ID}:${IMG_TAG} \
bash
@@ -59,7 +62,7 @@ build-ucentral-docker-img: build-ucentral-app
cp docker/deliverables/ucentral-client docker/
cp docker/deliverables/rtty docker/
OLDIMG=$$(docker images --format "{{.ID}}" ucentral-client:latest)
docker build --file docker/Dockerfile --tag ucentral-client:latest docker
docker build --file docker/Dockerfile --tag ucentral-client:latest docker --label com.azure.sonic.manifest="$$(cat docker/manifest.json)"
NEWIMG=$$(docker images --format "{{.ID}}" ucentral-client:latest)
if [ -n "$$OLDIMG" ] && [ ! "$$OLDIMG" = "$$NEWIMG" ]; then
docker image rm $$OLDIMG
@@ -81,14 +84,39 @@ build-final-deb: build-ucentral-docker-img
@echo
@echo "ucentral client deb pkg is available under ./output/ dir"
plat-ec:
src/ec-private/build.sh
build-arm64-deb: build-ucentral-docker-img
docker inspect --type=image ${DPKG_IMAGE} >/dev/null 2>&1 || \
docker build --file dpkg-builder.Dockerfile --tag ${DPKG_IMAGE} .
docker container stop ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container rm ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container run -d -t \
--name ${DPKG_CONTAINER_NAME} \
--platform linux/arm64 \
-v $(realpath ./):$(realpath ./) \
-w $(realpath ./src/) \
--user $(shell id -u):$(shell id -g) \
--tmpfs /tmp \
${DPKG_IMAGE} \
bash
docker exec -t ${DPKG_CONTAINER_NAME} dpkg-buildpackage -rfakeroot -b -us -uc -j
mv ucentral-client*deb ./output/
mv src/docker-ucentral-client.gz ./output/
docker container stop ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container rm ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
clean:
docker container stop ${CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container rm ${CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container stop ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
docker container rm ${DPKG_CONTAINER_NAME} > /dev/null 2>&1 || true;
docker rmi ucentral-client 2>/dev/null || true;
docker rmi ${IMG_ID}:${IMG_TAG} 2>/dev/null || true;
docker rmi ${DPKG_IMAGE} 2>/dev/null || true;
rm -rf output 2>/dev/null || true;
rm -rf docker 2>/dev/null || true;
rm -rf src/docker/deliverables || true;
@@ -99,14 +127,4 @@ clean:
rm -rf src/debian/.debhelper src/debian/ucentral-client 2>/dev/null || true;
rm -rf src/debian/shasta-ucentral-client* 2>/dev/null || true;
rm -rf src/debian/debhelper-build-stamp* 2>/dev/null || true;
rm -rf src/debian/files shasta_1.0_amd64.changes shasta_1.0_amd64.buildinfo 2>/dev/null || true;
plat-ec-clean:
rm -rf src/ec-private/cjson
rm -rf src/ec-private/curl
rm -rf src/ec-private/libwebsockets
rm -rf src/ec-private/openssl
rm -rf src/ec-private/openssl
rm -rf src/ec-private/ecapi/build
rm -rf src/ec-private/ucentral
rm -rf output
rm -rf src/debian/files shasta_1.0_arm64.changes shasta_1.0_arm64.buildinfo 2>/dev/null || true;

View File

@@ -11,7 +11,7 @@ cfg2:
cfg3:
Bring ports 1 up, 2 up (Ethernet1, Ethernet2) (admin state);
Destroy any VLAN that is not in the list (in this particular CFG - create VLAN 10,
destroye any other, except for MGMT VLAN 1 - it's not being altered by the
destroy any other, except for MGMT VLAN 1 - it's not being altered by the
uCentral app itself);
Create VLAN 10;
Set VLAN 10 memberlist with the following ports: Ethernet1, Ethernet2;
@@ -39,6 +39,7 @@ cfg5_poe:
- detection mode is 4pt-dot3af;
- power limit is 99900mW (e.g. max per port);
- priority is LOW;
cfg7_ieee80211x.json:
Following json file configures the given topology:
+-----------------+
@@ -64,3 +65,33 @@ cfg7_ieee80211x.json:
to be the same for the given (10.10.20.0/24) network.
.1x client also must have a valid credentials data (both client and radius server
must have same clients credentials configured).
cfg_igmp.json:
Configure igmp snooping and querier on VLAN 1.
Configure igmp static groups:
- 230.1.1.1 with egress port Ethernet1
- 230.2.2.2 with egress ports Ethernet2 & Ethernet3
cfg_rpvstp.json:
Configure VLAN 1;
Configure VLAN 2;
Configure rapid per-vlan STP on VLAN 1 with priority 32768;
Disable STP on VLAN 2.
cfg_port_isolation.json:
Configure port isolation with Ethernet1 as uplink and
Ethernet2 & Ethernet3 as downlink
cfg_services_log.json:
Enable syslog with these parameters:
- remote host addr
- remote host port
- log severity (priority):
* emerg: 0
* alert: 1
* crit: 2
* error: 3
* warning: 4
* notice: 5
* info: 6
* debug: 7

View File

@@ -1,70 +1,70 @@
{
"ethernet": [
{
"duplex": "full",
"enabled": false,
"speed": 1000,
"select-ports": [
"Ethernet0",
"Ethernet1",
"Ethernet2",
"Ethernet3",
"Ethernet4",
"Ethernet5",
"Ethernet6",
"Ethernet7",
"Ethernet8",
"Ethernet9",
"Ethernet10",
"Ethernet11",
"Ethernet12",
"Ethernet13",
"Ethernet14",
"Ethernet15",
"Ethernet16",
"Ethernet17",
"Ethernet18",
"Ethernet19",
"Ethernet20",
"Ethernet21",
"Ethernet22",
"Ethernet23",
"Ethernet24",
"Ethernet25",
"Ethernet26",
"Ethernet27",
"Ethernet28",
"Ethernet29",
"Ethernet30",
"Ethernet31",
"Ethernet32",
"Ethernet33",
"Ethernet34",
"Ethernet35",
"Ethernet36",
"Ethernet37",
"Ethernet38",
"Ethernet39",
"Ethernet40",
"Ethernet41",
"Ethernet42",
"Ethernet43",
"Ethernet44",
"Ethernet45",
"Ethernet46",
"Ethernet47",
"Ethernet48",
"Ethernet52",
"Ethernet56",
"Ethernet60",
"Ethernet64",
"Ethernet68",
"Ethernet72",
"Ethernet76"
]
}
],
"interfaces": [],
"services": {},
"uuid": 1
}
{
"ethernet": [
{
"duplex": "full",
"enabled": false,
"speed": 1000,
"select-ports": [
"Ethernet0",
"Ethernet1",
"Ethernet2",
"Ethernet3",
"Ethernet4",
"Ethernet5",
"Ethernet6",
"Ethernet7",
"Ethernet8",
"Ethernet9",
"Ethernet10",
"Ethernet11",
"Ethernet12",
"Ethernet13",
"Ethernet14",
"Ethernet15",
"Ethernet16",
"Ethernet17",
"Ethernet18",
"Ethernet19",
"Ethernet20",
"Ethernet21",
"Ethernet22",
"Ethernet23",
"Ethernet24",
"Ethernet25",
"Ethernet26",
"Ethernet27",
"Ethernet28",
"Ethernet29",
"Ethernet30",
"Ethernet31",
"Ethernet32",
"Ethernet33",
"Ethernet34",
"Ethernet35",
"Ethernet36",
"Ethernet37",
"Ethernet38",
"Ethernet39",
"Ethernet40",
"Ethernet41",
"Ethernet42",
"Ethernet43",
"Ethernet44",
"Ethernet45",
"Ethernet46",
"Ethernet47",
"Ethernet48",
"Ethernet52",
"Ethernet56",
"Ethernet60",
"Ethernet64",
"Ethernet68",
"Ethernet72",
"Ethernet76"
]
}
],
"interfaces": [],
"services": {},
"uuid": 1
}

View File

@@ -1,16 +1,16 @@
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [],
"services": {},
"uuid": 1
}
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [],
"services": {},
"uuid": 1
}

View File

@@ -1,23 +1,23 @@
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1"
]
},
{
"duplex": "full",
"enabled": false,
"select-ports": [
"Ethernet2"
],
"speed": 1000
}
],
"interfaces": [],
"services": {},
"uuid": 2
}
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1"
]
},
{
"duplex": "full",
"enabled": false,
"select-ports": [
"Ethernet2"
],
"speed": 1000
}
],
"interfaces": [],
"services": {},
"uuid": 2
}

View File

@@ -1,35 +1,35 @@
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [
{
"vlan": {
"id": 10,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet1",
"Ethernet2"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
}
],
"services": {},
"uuid": 3
}
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [
{
"vlan": {
"id": 10,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet1",
"Ethernet2"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
}
],
"services": {},
"uuid": 3
}

View File

@@ -1,51 +1,51 @@
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [
{
"vlan": {
"id": 10,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet1"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
},
{
"vlan": {
"id": 100,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet2"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
}
],
"services": {},
"uuid": 3
}
{
"ethernet": [
{
"duplex": "full",
"enabled": true,
"speed": 1000,
"select-ports": [
"Ethernet1",
"Ethernet2"
]
}
],
"interfaces": [
{
"vlan": {
"id": 10,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet1"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
},
{
"vlan": {
"id": 100,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet2"
],
"vlan-tag": "tagged"
}
],
"name": "mgmt",
"role": "upstream",
"services": []
}
],
"services": {},
"uuid": 3
}

View File

@@ -17,7 +17,11 @@
{
"ipv4": {
"addressing": "static",
"subnet": "20.20.20.20/24",
"subnet": [
{
"prefix": "20.20.20.20/24"
}
],
"dhcp": {
"relay-server": "172.20.254.8",
"circuit-id-format": "{Name}:{VLAN-ID}"
@@ -44,7 +48,11 @@
{
"ipv4": {
"addressing": "static",
"subnet": "30.30.30.30/24",
"subnet": [
{
"prefix": "30.30.30.30/24"
}
],
"dhcp": {
"relay-server": "172.20.10.12",
"circuit-id-format": "{Name}:{VLAN-ID}"
@@ -71,7 +79,11 @@
{
"ipv4": {
"addressing": "static",
"subnet": "172.20.10.181/24"
"subnet": [
{
"prefix": "172.20.10.181/24"
}
]
},
"vlan": {
"id": 20,

View File

@@ -50,7 +50,11 @@
},
"ipv4": {
"addressing": "static",
"subnet": "10.10.20.100/24"
"subnet": [
{
"prefix": "10.10.20.100/24"
}
]
},
"ethernet": [
{
@@ -70,7 +74,11 @@
},
"ipv4": {
"addressing": "static",
"subnet": "10.10.50.100/24"
"subnet": [
{
"prefix": "10.10.50.100/24"
}
]
},
"ethernet": [
{

View File

@@ -0,0 +1,64 @@
{
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"speed": 1000,
"duplex": "full",
"enabled": true,
"poe": {
"admin-mode": true
}
}
],
"interfaces": [
{
"vlan": {
"id": 1,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"vlan-tag": "un-tagged"
}
],
"ipv4": {
"multicast": {
"igmp": {
"querier-enable": true,
"query-interval": 60,
"snooping-enable": true,
"version": 3,
"static-mcast-groups": [
{
"address": "230.1.1.1",
"egress-ports": [
"Ethernet1"
]
},
{
"address": "230.2.2.2",
"egress-ports": [
"Ethernet2",
"Ethernet3"
]
}
]
}
},
"subnet": [
{
"prefix": "1.1.1.1/24"
}
]
},
"role": "upstream",
"name": "mgmt-vlan"
}
],
"uuid": 1
}

View File

@@ -0,0 +1,54 @@
{
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"speed": 1000,
"duplex": "full",
"enabled": true,
"poe": {
"admin-mode": true
}
}
],
"interfaces": [
{
"vlan": {
"id": 1,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"vlan-tag": "un-tagged"
}
],
"role": "upstream",
"name": "mgmt-vlan"
}
],
"switch": {
"port-isolation": {
"sessions": [
{
"id": 1,
"uplink": {
"interface-list": [
"Ethernet1"
]
},
"downlink": {
"interface-list": [
"Ethernet2",
"Ethernet3"
]
}
}
]
}
},
"uuid": 1
}

View File

@@ -0,0 +1,66 @@
{
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"speed": 1000,
"duplex": "full",
"enabled": true,
"poe": {
"admin-mode": true
}
}
],
"interfaces": [
{
"vlan": {
"id": 1,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"vlan-tag": "un-tagged"
}
],
"role": "upstream",
"name": "mgmt-vlan"
},
{
"vlan": {
"id": 2,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"vlan-tag": "tagged"
}
],
"role": "upstream",
"name": "mgmt-vlan"
}
],
"switch": {
"loop-detection": {
"protocol": "rpvstp",
"instances": [
{
"id": 1,
"enabled": true,
"priority": 32768
},
{
"id": 2,
"enabled": false
}
]
}
},
"uuid": 1
}

View File

@@ -0,0 +1,43 @@
{
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"speed": 1000,
"duplex": "full",
"enabled": true,
"poe": {
"admin-mode": true
}
}
],
"interfaces": [
{
"vlan": {
"id": 1,
"proto": "802.1q"
},
"ethernet": [
{
"select-ports": [
"Ethernet*"
],
"vlan-tag": "un-tagged"
}
],
"role": "upstream",
"name": "mgmt-vlan"
}
],
"services": {
"log": {
"port": 2000,
"priority": 7,
"size": 1000,
"host": "192.168.1.10",
"proto": "udp"
}
},
"uuid": 1
}

8
dpkg-builder.Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM arm64v8/debian:buster
RUN apt-get update -q -y && apt-get -q -y --no-install-recommends install \
build-essential \
fakeroot \
dpkg-dev \
dh-exec \
debhelper

View File

@@ -8,7 +8,7 @@ export DH_VERBOSE = 1
export DH_BUILD_DDEBS=1
export DEB_BUILD_OPTIONS=autodbgsym
CONFIGURED_ARCH ?= amd64
CONFIGURED_ARCH ?= arm64
UCENTRAL_CLIENT_VERSION ?= 1.0
DPKG_EXPORT_BUILDFLAGS = 1
INSTALL ?= debian/ucentral-client/
@@ -36,6 +36,7 @@ override_dh_install:
# home folder.
mkdir -p ${INSTALL}/home/admin
cp scripts/OLS_NOS_fixups.script ${INSTALL}/usr/local/lib
cp scripts/OLS_NOS_upgrade_override.script ${INSTALL}/usr/local/lib
cp docker-ucentral-client.gz ${INSTALL}/usr/local/lib
# Install Vlan1 in-band management configuration
mkdir -p ${INSTALL}/etc/network/interfaces.d/

View File

@@ -1,4 +1,4 @@
FROM debian:buster
FROM arm64v8/debian:buster
RUN echo "uCentral client support"
RUN apt-get update && apt-get install --no-install-recommends -y \
@@ -9,6 +9,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
curl \
libjsoncpp-dev \
busybox \
libhiredis0.14 \
&& rm -rf /var/lib/apt/lists/*
RUN ln -s /bin/busybox /usr/local/bin/nslookup
@@ -21,6 +22,11 @@ COPY /ucentral-client /usr/local/bin/ucentral-client
COPY /rtty /usr/local/bin/
COPY /lib* /usr/local/lib/
# Install root CA certificate for development purposes
COPY /ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt
COPY /tip-cert.pem /usr/local/share/ca-certificates/tip-cert.crt
RUN update-ca-certificates
RUN ldconfig
RUN ls -l /usr/local/bin/ucentral-client

35
src/docker/ca-cert.pem Normal file
View File

@@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGHzCCBAegAwIBAgIUe4hxamQ8YDS/GuFPIN+uqH4BgMswDQYJKoZIhvcNAQEL
BQAwgZ4xCzAJBgNVBAYTAlVBMRQwEgYDVQQIDAtLeWl2IE9ibGFzdDENMAsGA1UE
BwwES3lpdjEbMBkGA1UECgwSTGFyY2ggTmV0d29ya3MgTHRkMREwDwYDVQQLDAhT
b2Z0d2FyZTERMA8GA1UEAwwIY2EubGFyY2gxJzAlBgkqhkiG9w0BCQEWGGFkbWlu
QGxhcmNoLW5ldHdvcmtzLmNvbTAeFw0yNDA0MDQwNzUxMDlaFw0zNDA0MDIwNzUx
MDlaMIGeMQswCQYDVQQGEwJVQTEUMBIGA1UECAwLS3lpdiBPYmxhc3QxDTALBgNV
BAcMBEt5aXYxGzAZBgNVBAoMEkxhcmNoIE5ldHdvcmtzIEx0ZDERMA8GA1UECwwI
U29mdHdhcmUxETAPBgNVBAMMCGNhLmxhcmNoMScwJQYJKoZIhvcNAQkBFhhhZG1p
bkBsYXJjaC1uZXR3b3Jrcy5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQDG5kue5pYz+PC/NBZpBesN2NjWzpcTAunzFnQQ/XAMY9XLcUdyrXLz6r2p
bFbmUiW1am+2TZMg6sQbBPId7vjuHZykXavgUd8ybUoQ2GHnWVsCCy8b/Fn2WF2r
Zc14ffRncZ4BsHCPJsWboCmYVsgNgr1nmDT6Le5VIRJ2MJksuTz+55bK9qCIIWWC
PFBpGjMHpEdybvwa5gzv2Do9vwb0m76LKL5ygyle4hFoI54JwRV8O7g8FishTrmI
oSDmapB7+K9egK2wXhbHQ6pIc4qvs9YdSPaTkLV/tRa3Oyw+vUGDW5Bg5UezPJNf
U9yCZIjKZXYXbqaZRgtcXJfqBGFlmuVX44zfjpEe2ovHXrb7HokZxEcJA/yooxzw
3eZXDxsdVXU02jA9jSQjX1Q44lwxUn1wVZlTdr9AUJM6JiEujITy2fwyfaeTwCyS
Y4xU7qgMr9Wml0kHPPFnlsTWo+dswpztRt5BKCKMcsyLETiLkapsI784ameiEgpA
3klYJsMh/2+Q9ABuK5BQOuHCPvalNoEKn4rUyQgdTL/ay3oqlvNf4r08hgHA72Aj
oM60Ldq2gJeuaEh6l3fV182qd4funDgrMpQpfRcIKDJcDRuN8vKzqmTXLNY8zgNY
xharAsEm3CdBwbpTF4E1yDCH8seuj3TCcMwJAWGq73QRgu0DwQIDAQABo1MwUTAd
BgNVHQ4EFgQUwqiRcQgGkVIdKjDjA3KCVTl0rTwwHwYDVR0jBBgwFoAUwqiRcQgG
kVIdKjDjA3KCVTl0rTwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AgEAwUEtLkinQmWmRqQfKZayUDjKl0B7HZy9en0LI6WzKppfj9OwZcAxWNqavDwp
WRCEmzt27NPF42C6lr/i0ZBOT5V1qDoP8/JR3QW/dsEIS2wW61803pw77U8SyfKP
tEwbQsCuw7jBCSDjDKI5k9aBlltHzTzSKjo1nBTwMYHho1dR5Th6mOXr9soyNopU
l7SlvC5UWuCYxhxjWlZnxaupbKdPNt88SPWDLWa6HLBil2XSu/5wMVdubyCZrlN/
8SajdHtev9lk3pucnLmqxCq6sChim9EzDcVVRPNzR59H6dbyMec16e0XrnNcd9ch
2Zo2HQm3jyIh4YL1iRrHyqJgVfmI1hKlvqUOfAxVCV8pSec40EQD2sd1Ges5QTiH
LCk2osPvDjV1JyFKcr5Pf50+S02MViU8tza+VfUwozRa6A9qR/JlBTBlCrDEYexm
CK7I11Qln26vvtlnJTC7OzXTaOSYQ1NlJCwLwsMOvhYVm0skxL1HX1AYHKZ0t0QW
PAQYij2QP1YK4SU68MlXiE3D5N7vZ+42pfHx0DNZXlsudM7kRmkT3FBIh54mh4xt
0a5nUDjd3IBPcVAeVJZqbxglAwKlue+MhJRKSFxehE3qEKurmH1tGUTb5NnmMllC
6G51ZGoyb64oGejymIc2IB5rXH5+Uu2HUsyoVLt0wihgU7E=
-----END CERTIFICATE-----

View File

@@ -4,7 +4,8 @@
"package": {
"version": "1.0.0",
"depends": [],
"name": "ucentral_client"
"name": "ucentral_client",
"description": "SONiC uCentral client package"
},
"service": {
"name": "ucentral_client",
@@ -25,12 +26,24 @@
},
"container": {
"privileged": false,
"volumes": [],
"volumes": ["/var/lib/ucentral:/var/lib/ucentral"],
"mounts": [
{
"source": "TCA",
"target": "/etc/ucentral",
"type": "volume",
"volume-opt": {
"device": "/dev/disk/by-label/ONIE-TIP-CA-CERT",
"type": "ext4",
"o": "ro"
}
}
],
"tmpfs": []
},
"cli": {
"config": "",
"show": "",
"clear": ""
"config": [],
"show": [],
"clear": []
}
}

49
src/docker/tip-cert.pem Normal file
View File

@@ -0,0 +1,49 @@
-----BEGIN CERTIFICATE-----
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
5IOM7ItsRmen6u3qu+JXros54e4juQ==
-----END CERTIFICATE-----

View File

@@ -1,33 +0,0 @@
# Ucentral for EC
Ucentral solution for EC is made of the following parts:
* `ecapi`: a library to communicate with EC via SNMP
# Compiling
## EC Build for Target Device
First build the full EC image for your target device:
* `cd EC_VOB/project_build_environment/<target device>`
* `./make_all`
If this is successful, you can proceed to the next step.
## Build Environment
To successfully build required components the build environments variables must be prepared:
* `cd EC_VOB/project_build_environment/<target device>`
* `cd utils`
* `. build_env_init`
## Building All Components
Presumably you have checked out the [ols-ucentral-src]:
* `cd [ols-ucentral-src]`
* Run `make plat-ec`, which should successfully compile all components
## Creating EC Firmware with Ucentral
After building everything up:
* Check the `output` directory, it should contain all required binaries in appropriate subdirectories
* Copy over these directories to your `EC_VOB/project_build_environment/<target device>/user/thirdpty/ucentral`

View File

@@ -1,141 +0,0 @@
#!/bin/bash
UCENTRAL_DIR=${PWD}
EC_BUILD_DIR=${PWD}/src/ec-private
OUT_DIR=${UCENTRAL_DIR}/output
BIN_DIR=${OUT_DIR}/usr/sbin
LIB_DIR=${OUT_DIR}/lib
LIB_OPENSSL=openssl-1.1.1q
LIB_WEBSOCKETS=libwebsockets-4.1.4
LIB_CURL=curl-7.83.1
LIB_CJSON=cJSON-1.7.15
echo "+++++++++++++++++ check EC build environment +++++++++++++++++"
if [ ! "${PROJECT_NAME}" ] || [ ! "${SOURCE_PATH}" ]; then
echo "Error! Please source 'build_env_init' for your build environment."
exit
fi
cp -af ${UCENTRAL_DIR}/src/ucentral-client/* ${EC_BUILD_DIR}/ucentral-client
rm -rf ${OUT_DIR}
if [ ! -d output ]; then
mkdir -p ${BIN_DIR}
mkdir -p ${LIB_DIR}
fi
C_COMPILER="${TOOLCHAIN_PATH}/${CROSS_COMPILE}gcc ."
echo "+++++++++++++++++ openssl +++++++++++++++++"
cd ${EC_BUILD_DIR}
if [ ! -d openssl ]; then
tar -xf ./archive/${LIB_OPENSSL}.tar.gz
mv ${LIB_OPENSSL} openssl
fi
model_name=${D_MODEL_NAME}
if [ "$model_name" == 'ECS4130_AC5' ]; then
platform=linux-aarch64
elif [ "$model_name" == 'ECS4125_10P' ]; then
platform=linux-mips32
else
echo "Error! The model ${model_name} is not in the support lists, please check."
exit 1
fi
cd openssl
./Configure ${platform} --cross-compile-prefix=${CROSS_COMPILE} no-idea no-mdc2 no-rc5 no-ssl2 no-ssl3
make -j${nproc}
if [ "$?" -eq "0" ]; then
cp -af libssl.so.1.1 libcrypto.so.1.1 ${LIB_DIR}
fi
echo "+++++++++++++++++ libwebsockets +++++++++++++++++"
cd ${EC_BUILD_DIR}
if [ ! -d libwebsockets ]; then
tar -xf ./archive/${LIB_WEBSOCKETS}.tar.gz
mv ${LIB_WEBSOCKETS} libwebsockets
patch -s -N -p1 -d libwebsockets/lib < ./patch/libwebsockets/${LIB_WEBSOCKETS}.patch
fi
cd libwebsockets
cmake \
-DOPENSSL_ROOT_DIR=${EC_BUILD_DIR}/openssl \
-DCMAKE_C_COMPILER=${C_COMPILER}
make -j${nproc}
if [ "$?" -eq "0" ]; then
cp -af lib/libwebsockets.so.17 ${LIB_DIR}
fi
echo "+++++++++++++++++ curl +++++++++++++++++"
cd ${EC_BUILD_DIR}
if [ ! -d curl ]; then
tar -xf ./archive/${LIB_CURL}.tar.xz
mv ${LIB_CURL} curl
patch -s -N -p1 -d curl < ./patch/curl/${LIB_CURL}.patch
fi
cd curl
cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_SHARED_LINKER_FLAGS=-L${EC_BUILD_DIR}/openssl
make
if [ "$?" -eq "0" ]; then
cp -af ./lib/libcurl.so ${LIB_DIR}
cp -af ./src/curl ${BIN_DIR}
fi
echo "+++++++++++++++++ cjson +++++++++++++++++"
cd ${EC_BUILD_DIR}
if [ ! -d cjson ]; then
tar -xf ./archive/${LIB_CJSON}.tar.gz
mv ${LIB_CJSON} cjson
fi
cd cjson
cmake -DCMAKE_C_COMPILER=${C_COMPILER}
make
if [ "$?" -eq "0" ]; then
cp -af ./libcjson.so.1.7.15 ${LIB_DIR}
cd ${LIB_DIR}
mv libcjson.so.1.7.15 libcjson.so.1
fi
echo "+++++++++++++++++ ecapi +++++++++++++++++"
cd ${EC_BUILD_DIR}/ecapi
mkdir ${EC_BUILD_DIR}/ecapi/build
cd ${EC_BUILD_DIR}/ecapi/build
cmake -DCMAKE_C_COMPILER=${C_COMPILER} ..
make
if [ "$?" -eq "0" ]; then
cp -af libecapi.so ${LIB_DIR}
fi
echo "+++++++++++++++++ ucentral-client +++++++++++++++++"
if [ ! -d ucentral ]; then
mkdir -p ${EC_BUILD_DIR}/ucentral
fi
cp -af ${UCENTRAL_DIR}/src/ucentral-client ${EC_BUILD_DIR}/ucentral/ucentral-client
cp -af ${EC_BUILD_DIR}/patch/ucentral/* ${EC_BUILD_DIR}/ucentral
mkdir -p ${EC_BUILD_DIR}/ucentral/build
cd ${EC_BUILD_DIR}/ucentral/build
cmake -DCMAKE_C_COMPILER=${C_COMPILER} ..
make
if [ "$?" -eq "0" ]; then
cp -af ucentral-client ${BIN_DIR}
fi
echo "+++++++++++++++++ Strip target binaries +++++++++++++++++"
${TOOLCHAIN_PATH}/${CROSS_COMPILE}strip ${BIN_DIR}/*
${TOOLCHAIN_PATH}/${CROSS_COMPILE}strip ${LIB_DIR}/*

View File

@@ -1,31 +0,0 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(ecapi C)
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude/mibconstants)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude/oem/$ENV{PROJECT_NAME})
INCLUDE_DIRECTORIES($ENV{PROJECT_PATH}/user/thirdpty/lua/net-snmp-5.4.4/include)
INCLUDE_DIRECTORIES($ENV{PROJECT_PATH}/user/thirdpty/lua/net-snmp-5.4.4/agent/mibgroup)
#LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/snmp)
FIND_LIBRARY(netsnmp_library netsnmp $ENV{PROJECT_PATH}/user/thirdpty/lua/net-snmp-5.4.4/snmplib/.libs)
#INCLUDE (CheckSymbolExists)
#CHECK_SYMBOL_EXISTS(SYS_getrandom syscall.h getrandom)
if ($ENV{D_MODEL_NAME} STREQUAL ECS4130_AC5)
add_definitions(-DENDIANNESS_ADJUST)
endif()
INCLUDE(snmp/CMakeLists.txt)
INCLUDE(generic/CMakeLists.txt)
ADD_LIBRARY(ecapi SHARED ${LIB_SOURCES})
TARGET_LINK_LIBRARIES(ecapi ${netsnmp_library})

View File

@@ -1,3 +0,0 @@
list(APPEND LIB_SOURCES
${CMAKE_CURRENT_LIST_DIR}/api_print.c
)

View File

@@ -1,27 +0,0 @@
// #include <stdarg.h>
#include "api_print.h"
static bool debug_on = false;
void print_set_debug(bool on) {
debug_on = on;
}
bool print_is_debug(void) {
return debug_on;
}
/*
void print_debug(char *fmt, ...) {
if (print_is_debug()) {
va_list args; va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
}
}
void print_err(char *fmt, ...) {
va_list args; va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}*/

View File

@@ -1,40 +0,0 @@
#ifndef API_CONFIG_H
#define API_CONFIG_H
#include <stdbool.h>
#include <stdint.h>
typedef enum {
DPX_HALF = 0,
DPX_FULL,
} duplex_t;
typedef enum {
M_NONE = 1,
M_SFP_FORCED_1000 = 7,
M_SFP_FORCED_10G = 8,
} media_t;
typedef enum {
VL_NONE = 0,
VL_TAGGED,
VL_UNTAGGED,
VL_FORBIDDEN
} vlan_membership_t;
void *open_config_transaction();
void commit_config_transaction(void *tr);
void add_eth_speed(void *tr, uint16_t eth_num, uint32_t speed, duplex_t duplex);
void add_eth_media(void *tr, uint16_t eth_num, media_t media);
void add_l2_vlan(void *tr, uint16_t vlan_id,
uint16_t *tagged_members, // NULL terminated array / NULL if not required
uint16_t *un_tagged_members, // NULL terminated array / NULL if not required
uint16_t *forbidden_members, // NULL terminated array / NULL if not required
uint16_t *pvid_ports // NULL terminated array / NULL if not required
);
#endif

View File

@@ -1,8 +0,0 @@
#ifndef API_CONSTS_H
#define API_CONSTS_H
#define STATUS_SUCCESS 0
#define STATUS_ERROR 1
#define STATUS_TIMEOUT 2
#endif

View File

@@ -1,15 +0,0 @@
#ifndef API_DEVICEID_H
#define API_DEVICEID_H
#include <stdint.h>
#include "api_consts.h"
int dev_get_main_mac(char *mac, int mac_len);
int dev_get_serial(char *serial, int serial_len);
int dev_get_fw_version(char *fw, int fw_len);
int dev_get_uptime(uint32_t *up);
int dev_get_vlan_list(int *vlan_arr, int *num);
int dev_get_vlan_mask_len(int *len);
int dev_get_poe_port_num(int *num);
int dev_get_port_capabilities_val_len(int *len);
#endif

View File

@@ -1,13 +0,0 @@
#ifndef API_PRINT_H
#define API_PRINT_H
#include <stdio.h>
#include <stdbool.h>
void print_set_debug(bool on);
bool print_is_debug(void);
#define print_debug(...) if (print_is_debug()) { fprintf(stdout, __VA_ARGS__); }
#define print_err(...) fprintf(stderr, __VA_ARGS__)
#endif

View File

@@ -1,9 +0,0 @@
#ifndef API_SESSION_H
#define API_SESSION_H
#include "api_consts.h"
int session_start(void);
void session_close(void);
#endif

View File

@@ -1,36 +0,0 @@
#ifndef API_STATS_H
#define API_STATS_H
#include <stdint.h>
#include <stdbool.h>
#include "api_consts.h"
#define IF_LOCATION_SIZE 16
#define IF_NAME_SIZE 32
typedef struct {
uint32_t collisions;
uint64_t multicast ;
uint64_t rx_bytes;
uint32_t rx_dropped;
uint32_t rx_errors;
uint64_t rx_packets;
uint64_t tx_bytes;
uint32_t tx_dropped;
uint32_t tx_errors;
uint64_t tx_packets;
} counters_t;
typedef struct {
char location[IF_LOCATION_SIZE];
char name[IF_NAME_SIZE];
uint32_t uptime;
uint32_t speed_dpx_status;
counters_t counters;
} interface_t;
int get_ethernet_count(int *eth_count);
int get_ethernet_stats(interface_t *eths, int eth_count);
int get_vlans(uint16_t **vlans, int *vlan_count);
#endif

View File

@@ -1,41 +0,0 @@
#ifndef OID_DEFINE_H
#define OID_DEFINE_H
#include <sys_adpt.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
const static oid O_MAIN_MAC[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 5, 6, 1, 0 };
const static oid O_SERIAL[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 1, 3, 1, 10, 1 };
const static oid O_OPCODE_VERSION[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 1, 5, 4, 0 };
const static oid O_SYS_UPTIME[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
const static oid O_VLAN_STATUS[] = { 1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1, 5};
const static oid O_POE_PORT_ENABLE[] ={1, 3, 6, 1, 2, 1, 105, 1, 1, 1, 3, 1};
const static oid O_PORT_CPAPBILITIES[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 2, 1, 1, 6, 1 };
#define O_FACTORY_DEFAULT SYSTEM_OID"1.24.2.1.1.4.1.70.97.99.116.111.114.121.95.68.101.102.97.117.108.116.95.67.111.110.102.105.103.46.99.102.103"
#define O_FW_UPGRADE_MGMT SYSTEM_OID"1.24.6.1.0"
#define O_DEVICE_MODEL SYSTEM_OID"1.1.5.1.0"
#define O_DEVICE_COMPANY SYSTEM_OID"1.1.5.2.0"
#define O_STR_POE_PORT_ENABLE "1.3.6.1.2.1.105.1.1.1.3.1"
#define O_STR_POE_MAX_POWER SYSTEM_OID"1.28.6.1.13.1"
#define O_STR_POE_USAGE_THRESHOLD "1.3.6.1.2.1.105.1.3.1.1.5.1"
#define O_STR_IF_ADMIN_STATUS "1.3.6.1.2.1.2.2.1.7"
#define O_STR_PORT_CPAPBILITIES SYSTEM_OID"1.2.1.1.6"
#define O_STR_PVID "1.3.6.1.2.1.17.7.1.4.5.1.1"
#define O_STR_VLAN_NAME "1.3.6.1.2.1.17.7.1.4.3.1.1"
#define O_STR_VLAN_EGRESS "1.3.6.1.2.1.17.7.1.4.3.1.2"
#define O_STR_VLAN_STATUS "1.3.6.1.2.1.17.7.1.4.3.1.5"
#define O_STR_VLAN_UNTAGGED "1.3.6.1.2.1.17.7.1.4.3.1.4"
#define O_STR_COPY_SRC_TYPE SYSTEM_OID"1.24.1.1.0"
#define O_STR_COPY_DST_TYPE SYSTEM_OID"1.24.1.3.0"
#define O_STR_COPY_DST_NAME SYSTEM_OID"1.24.1.4.0"
#define O_STR_COPY_FILE_TYPE SYSTEM_OID"1.24.1.5.0"
#define O_STR_COPY_ACTION SYSTEM_OID"1.24.1.8.0"
#define O_NTP_STATUS SYSTEM_OID"1.23.5.1.0"
#define O_SNTP_STATUS SYSTEM_OID"1.23.1.1.0"
#define O_SNTP_INTERVAL SYSTEM_OID"1.23.1.3.0"
#define O_SNTP_SERVER_TYPE SYSTEM_OID"1.23.1.4.1.4"
#define O_SNTP_SERVER_ADDR SYSTEM_OID"1.23.1.4.1.5"
#endif

View File

@@ -1,25 +0,0 @@
#ifndef SNMP_HELPER_H
#define SNMP_HELPER_H
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "oid_define.h"
int snmph_session_start(void);
void snmph_session_close(void);
int snmph_get(const oid *req_oid, size_t req_oid_len, struct snmp_pdu **response);
int snmph_get_argstr(const char *oid_str, struct snmp_pdu **response);
int snmph_get_single_string(const oid *req_oid, size_t req_oid_len, char *buf, int buf_len);
int snmph_get_bulk(const oid *req_oid, size_t req_oid_len, int max, struct snmp_pdu **response);
int snmph_set(const char *oid_str, char type, char *value);
int snmph_set_array(const char *oid_str, char type, const u_char *value, size_t len);
int snmph_walk(const char *oid_str, void *buf, int *num);
enum snmp_walk_node {
SNMP_WALK_NODE_NONE,
SNMP_WALK_NODE_VLAN_STATUS,
SNMP_WALK_NODE_POE_PORT_ENABLE,
};
#endif

View File

@@ -1,7 +0,0 @@
list(APPEND LIB_SOURCES
${CMAKE_CURRENT_LIST_DIR}/device.c
${CMAKE_CURRENT_LIST_DIR}/helper.c
${CMAKE_CURRENT_LIST_DIR}/session.c
${CMAKE_CURRENT_LIST_DIR}/stats.c
)

View File

@@ -1,96 +0,0 @@
#include <sys_adpt.h>
#include "api_device.h"
#include "snmp_helper.h"
int dev_get_main_mac(char *mac, int mac_len) {
int status = snmph_get_single_string(O_MAIN_MAC, OID_LENGTH(O_MAIN_MAC), mac, mac_len);
if (status != STAT_SUCCESS) {
return status;
}
int i = 0, j = 2;
for (i = 3; i < 17; i += 3) {
mac[j++] = mac[i];
mac[j++] = mac[i + 1];
}
mac[12] = 0;
char *c;
for (c = mac; *c; c++) {
if (*c >= 'A' && *c <= 'Z') {
*c += 32;
}
}
return STAT_SUCCESS;
}
int dev_get_serial(char *serial, int serial_len) {
return snmph_get_single_string(O_SERIAL, OID_LENGTH(O_SERIAL), serial, serial_len);
}
int dev_get_fw_version(char *fw, int fw_len) {
return snmph_get_single_string(O_OPCODE_VERSION, OID_LENGTH(O_OPCODE_VERSION), fw, fw_len);
}
int dev_get_uptime(uint32_t *up) {
struct snmp_pdu *response = NULL;
int status = snmph_get(O_SYS_UPTIME, OID_LENGTH(O_SYS_UPTIME), &response);
if (status != STATUS_SUCCESS) return status;
*up = (uint32_t) (response->variables->val.integer[0] / 100 + 0.5);
snmp_free_pdu(response);
return STATUS_SUCCESS;
}
int dev_get_vlan_list(int *vlan_arr, int *num) {
int status;
status = snmph_walk(O_STR_VLAN_STATUS, vlan_arr, num);
return status;
}
int dev_get_vlan_mask_len(int *len) {
char oidstr[MAX_OID_LEN];
struct snmp_pdu *response;
sprintf(oidstr, "%s.%d", O_STR_VLAN_EGRESS, 1);
int status = snmph_get_argstr(oidstr, &response);
if (status != STAT_SUCCESS) {
fprintf(stderr, "Could not retrieve vlan mask length.\n");
return status;
}
*len = response->variables->val_len;
return STATUS_SUCCESS;
}
int dev_get_poe_port_num(int *num) {
int status;
status = snmph_walk(O_STR_POE_PORT_ENABLE, 0, num);
return status;
}
int dev_get_port_capabilities_val_len(int *len) {
int status;
struct snmp_pdu *response = NULL;
status = snmph_get(O_PORT_CPAPBILITIES, OID_LENGTH(O_PORT_CPAPBILITIES), &response);
if (status == STATUS_SUCCESS)
*len = response->variables->val_len;
snmp_free_pdu(response);
return status;
}

View File

@@ -1,340 +0,0 @@
/* MODULE NAME: snmp_helper.c
* PURPOSE:
* for ucentral middleware process.
*
* NOTES:
*
* REASON:
* Description:
* HISTORY
* 2023/02/03 - Saulius P., Created
*
* Copyright(C) Accton Corporation, 2023
*/
/* INCLUDE FILE DECLARATIONS
*/
#include <math.h>
#include "snmp_helper.h"
#include "api_print.h"
static struct snmp_session session, *ss;
int snmph_session_start(void) {
init_snmp("ucmw_snmp");
snmp_sess_init( &session );
session.peername = "127.0.0.1";
session.version = SNMP_VERSION_2c;
session.community = (unsigned char*)"private";
session.community_len = strlen((char*)session.community);
ss = snmp_open(&session);
if (ss) {
return STAT_SUCCESS;
} else {
return STAT_ERROR;
}
}
int snmph_set(const char *oid_str, char type, char *value) {
netsnmp_pdu *pdu, *response = NULL;
size_t name_length;
oid name[MAX_OID_LEN];
int status, exitval = 0;
pdu = snmp_pdu_create(SNMP_MSG_SET);
name_length = MAX_OID_LEN;
if (snmp_parse_oid(oid_str, name, &name_length) == NULL){
snmp_perror(oid_str);
return -1;
} else{
if (snmp_add_var(pdu, name, name_length, type, value)) {
snmp_perror(oid_str);
return -1;
}
}
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS) {
if (response->errstat != SNMP_ERR_NOERROR) {
fprintf(stderr, "Error in packet.\nReason: %s\n",
snmp_errstring(response->errstat));
exitval = 2;
}
} else if (status == STAT_TIMEOUT) {
fprintf(stderr, "Timeout: No Response from %s\n",
session.peername);
exitval = 1;
} else { /* status == STAT_ERROR */
snmp_sess_perror("snmpset", ss);
exitval = 1;
}
if (response)
snmp_free_pdu(response);
return exitval;
}
int snmph_set_array(const char *oid_str, char type, const u_char *value, size_t len) {
netsnmp_pdu *pdu, *response = NULL;
size_t name_length;
oid name[MAX_OID_LEN];
int status, exitval = 0;
pdu = snmp_pdu_create(SNMP_MSG_SET);
name_length = MAX_OID_LEN;
if (snmp_parse_oid(oid_str, name, &name_length) == NULL){
snmp_perror(oid_str);
return -1;
} else{
if (!snmp_pdu_add_variable(pdu, name, name_length, type, value, len)) {
snmp_perror(oid_str);
return -1;
}
}
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS) {
if (response->errstat != SNMP_ERR_NOERROR) {
fprintf(stderr, "Error in packet.\nReason: %s\n",
snmp_errstring(response->errstat));
exitval = 2;
}
} else if (status == STAT_TIMEOUT) {
fprintf(stderr, "Timeout: No Response from %s\n",
session.peername);
exitval = 1;
} else { /* status == STAT_ERROR */
snmp_sess_perror("snmpset", ss);
exitval = 1;
}
if (response)
snmp_free_pdu(response);
return exitval;
}
int snmph_get(const oid *req_oid, size_t req_oid_len, struct snmp_pdu **response) {
struct snmp_pdu *request = snmp_pdu_create(SNMP_MSG_GET);
snmp_add_null_var(request, req_oid, req_oid_len);
int status = snmp_synch_response(ss, request, response);
if (*response && (*response)->errstat != SNMP_ERR_NOERROR) {
print_err("Error 1, response with error: %d, %ld\n", status, (*response)->errstat);
snmp_free_pdu(*response);
return STAT_ERROR;
}
if (!(*response)) {
print_err("Error 2: empty SNMP response\n");
return STAT_ERROR;
}
if (status != STAT_SUCCESS) {
print_err("Error 3: bad response status: %d\n", status);
snmp_free_pdu(*response);
}
if (!(*response)->variables) {
print_err("Error 4: empty variable list in response\n");
snmp_free_pdu(*response);
return STAT_ERROR;
}
print_debug("Default return: %d\n", status);
return status;
}
int snmph_get_argstr(const char *oid_str, struct snmp_pdu **response) {
oid name[MAX_OID_LEN];
size_t name_length = MAX_OID_LEN;
if (snmp_parse_oid(oid_str, name, &name_length) == NULL) {
snmp_perror(oid_str);
return -1;
}
struct snmp_pdu *request = snmp_pdu_create(SNMP_MSG_GET);
snmp_add_null_var(request, name, name_length);
int status = snmp_synch_response(ss, request, response);
if (*response && (*response)->errstat != SNMP_ERR_NOERROR) {
print_err("Error 1, response with error: %d, %ld\n", status, (*response)->errstat);
snmp_free_pdu(*response);
return STAT_ERROR;
}
if (!(*response)) {
print_err("Error 2: empty SNMP response\n");
return STAT_ERROR;
}
if (status != STAT_SUCCESS) {
print_err("Error 3: bad response status: %d\n", status);
snmp_free_pdu(*response);
}
if (!(*response)->variables) {
print_err("Error 4: empty variable list in response\n");
snmp_free_pdu(*response);
return STAT_ERROR;
}
print_debug("Default return: %d\n", status);
return status;
}
int snmph_get_single_string(const oid *req_oid, size_t req_oid_len, char *buf, int buf_len) {
struct snmp_pdu *response = NULL;
int status = snmph_get(req_oid, req_oid_len, &response);
if (status != STAT_SUCCESS) {
return status;
}
memset(buf, 0, buf_len);
strncpy(buf, (char*)response->variables->val.string, (int) fmin(buf_len, response->variables->val_len));
// if (response)
snmp_free_pdu(response);
return STAT_SUCCESS;
}
int snmph_get_bulk(const oid *req_oid, size_t req_oid_len, int max, struct snmp_pdu **response) {
struct snmp_pdu *request = snmp_pdu_create(SNMP_MSG_GETBULK);
request->non_repeaters = 0;
request->max_repetitions = max;
snmp_add_null_var(request, req_oid, req_oid_len);
int status = snmp_synch_response(ss, request, response);
// printf("Bulk status: %d\n", status);
if (status == 1) {
snmp_sess_perror("snmpbulkget", ss);
}
if (*response && (*response)->errstat != SNMP_ERR_NOERROR) {
print_err("Error 1, bulk response error: %d, %ld\n", status, (*response)->errstat);
snmp_free_pdu(*response);
return STAT_ERROR;
}
if (!(*response)) {
print_err("Error 2: empty bulk response\n");
return STAT_ERROR;
}
if (status != STAT_SUCCESS) {
print_err("Error 3, bad bulk status: %d\n", status);
snmp_free_pdu(*response);
}
if (!(*response)->variables) {
print_err("Error 4, empty bulk variables\n");
snmp_free_pdu(*response);
return STAT_ERROR;
}
print_debug("Default bulk return: %d\n", status);
return status;
}
int snmph_walk(const char *oid_str, void *buf, int *num) {
netsnmp_pdu *pdu, *response = NULL;
netsnmp_variable_list *vars;
oid name[MAX_OID_LEN];
size_t name_length = MAX_OID_LEN;
int running = 1;
int status = 0;
enum snmp_walk_node node = SNMP_WALK_NODE_NONE;
if (snmp_parse_oid(oid_str, name, &name_length) == NULL) {
snmp_perror(oid_str);
return -1;
}
if (!strcmp(oid_str, O_STR_VLAN_STATUS))
node = SNMP_WALK_NODE_VLAN_STATUS;
else if (!strcmp(oid_str, O_STR_POE_PORT_ENABLE))
node = SNMP_WALK_NODE_POE_PORT_ENABLE;
*num = 0;
while (running) {
/*
* create PDU for GETNEXT request and add object name to request
*/
pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
snmp_add_null_var(pdu, name, name_length);
/*
* do the request
*/
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS) {
if (response->errstat == SNMP_ERR_NOERROR) {
/*
* check resulting variables
*/
for (vars = response->variables; vars;
vars = vars->next_variable) {
if (node == SNMP_WALK_NODE_VLAN_STATUS)
{
if ((vars->name[12]==O_VLAN_STATUS[12]) && (vars->name_length==(OID_LENGTH(O_VLAN_STATUS)+1)))
{
((int*)buf)[(*num)++] = vars->name[13];
}
else
running = 0;
}
else if (node == SNMP_WALK_NODE_POE_PORT_ENABLE)
{
if ((vars->name[10]==O_POE_PORT_ENABLE[10]) && (vars->name_length==(OID_LENGTH(O_POE_PORT_ENABLE)+1)))
{
(*num)++;
}
else
running = 0;
}
else
running = 0;
memmove((char *) name, (char *) vars->name, vars->name_length * sizeof(oid));
name_length = vars->name_length;
//print_variable(vars->name, vars->name_length, vars);
}
} else {
running = 0;
}
} else if (status == STAT_TIMEOUT) {
fprintf(stderr, "Timeout: No Response from %s\n",
session.peername);
running = 0;
status = 1;
} else { /* status == STAT_ERROR */
snmp_sess_perror("snmpwalk", ss);
running = 0;
status = 1;
}
if (response)
snmp_free_pdu(response);
}
return status;
}
void snmph_session_close(void) {
snmp_close(ss);
}

View File

@@ -1,10 +0,0 @@
#include "api_session.h"
#include "snmp_helper.h"
int session_start() {
return snmph_session_start();
}
void session_close() {
snmph_session_close();
}

View File

@@ -1,250 +0,0 @@
#include <sys_adpt.h>
#include "api_device.h"
#include "api_stats.h"
#include "snmp_helper.h"
#include "if-mib/ifTable/ifTable_constants.h"
const static oid O_IF_COUNT[] = { 1, 3, 6, 1, 2, 1, 2, 1, 0 };
const static oid O_IF_TYPE[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 3 };
// const static oid O_IF_LAST_CHANGE[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 9 };
const static oid O_IF_UPTIME[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 2, 1, 1, 19 };
const static oid O_SPEED_DPX_STATUS[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 2, 1, 1, 8 };
const static oid OID_IF_NAME[] = { SYS_ADPT_PRIVATEMIB_OID, 1, 2, 1, 1, 2 };
const static oid O_IF_RX_BYTES_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6 };
const static oid O_IF_RX_DISCARD_PKTS[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 13 };
const static oid O_IF_RX_ERROR_PKTS[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 14 };
const static oid O_IF_RX_U_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 7 }; // Unicast packets
const static oid O_IF_RX_MUL_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 8 }; // Multicast packets
const static oid O_IF_RX_BR_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 9 };
const static oid O_IF_TX_BYTES_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 10 };
const static oid O_IF_TX_DISCARD_PKTS[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 19 };
const static oid O_IF_TX_ERROR_PKTS[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 20 };
const static oid O_IF_TX_U_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 11 }; // Unicast packets
const static oid O_IF_TX_MUL_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 12 }; // Multicast packets
const static oid O_IF_TX_BR_PKTS_64[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 13 };
int get_ethernet_count(int *eth_count) {
struct snmp_pdu *response;
// printf("Try to retrieve IF count...\n");
int status = snmph_get(O_IF_COUNT, OID_LENGTH(O_IF_COUNT), &response);
// printf("Retrieved: %d\n", status);
if (status != STAT_SUCCESS) {
// printf("Could not retrieve interfaces count\n");
return status;
}
// printf("Interfaces: %ld\n", response->variables->val.integer[0]);
long int max_if = response->variables->val.integer[0];
snmp_free_pdu(response);
struct variable_list *vars;
status = snmph_get_bulk(O_IF_TYPE, OID_LENGTH(O_IF_TYPE), max_if, &response);
if (status != STAT_SUCCESS) {
// printf("Could not retrieve types\n");
return STATUS_ERROR;
}
*eth_count = 0;
for(vars = response->variables; vars; vars = vars->next_variable) {
// print_variable(vars->name, vars->name_length, vars);
if (vars->val.integer[0] == IANAIFTYPE_ETHERNETCSMACD) {
(*eth_count)++;
} else {
break;
}
}
snmp_free_pdu(response);
return STATUS_SUCCESS;
}
static int fill_ethernet_stats_32(const oid *req_oid, size_t req_oid_len, int max, uint32_t *val, bool aggregate) {
struct snmp_pdu *response;
struct variable_list *vars;
int status = snmph_get_bulk(req_oid, req_oid_len, max, &response);
if (status != STATUS_SUCCESS) return status;
uint32_t *addr = val;
uint32_t local_val = 0;
int i = 0;
for(vars = response->variables; vars; vars = vars->next_variable) {
memcpy(&local_val, &vars->val.integer[0], sizeof(uint32_t));
addr = (uint32_t *) ((char *) val + (sizeof(interface_t) * (i++)));
if (aggregate) {
*addr += local_val;
} else {
*addr = local_val;
}
// addr = (uint32_t *) ((char *) addr + sizeof(interface_t));
}
snmp_free_pdu(response);
return STATUS_SUCCESS;
}
static int fill_ethernet_stats_64(const oid *req_oid, size_t req_oid_len, int max, uint64_t *val, bool aggregate) {
struct snmp_pdu *response;
struct variable_list *vars;
int status = snmph_get_bulk(req_oid, req_oid_len, max, &response);
if (status != STATUS_SUCCESS) return status;
uint64_t *addr = val;
uint64_t local_val = 0;
int i = 0;
for(vars = response->variables; vars; vars = vars->next_variable) {
#ifdef ENDIANNESS_ADJUST
memcpy(&local_val, &vars->val.counter64[0].low, sizeof(uint64_t));
#else
memcpy(&local_val, &vars->val.counter64[0], sizeof(uint64_t));
#endif
addr = (uint64_t *) ((char *) val + (sizeof(interface_t) * (i++)));
if (aggregate) {
*addr += local_val;
} else {
*addr = local_val;
}
// addr = (uint64_t *) ((char *) addr + sizeof(interface_t));
}
snmp_free_pdu(response);
return STATUS_SUCCESS;
}
int get_ethernet_stats(interface_t *eths, int eth_count) {
uint32_t uptime;
if (dev_get_uptime(&uptime) != STATUS_SUCCESS) return STATUS_ERROR;
/***************** Interface uptime *****************/
if (fill_ethernet_stats_32(O_IF_UPTIME, OID_LENGTH(O_IF_UPTIME), eth_count, &eths[0].uptime, false) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_32(O_SPEED_DPX_STATUS, OID_LENGTH(O_SPEED_DPX_STATUS), eth_count, &eths[0].speed_dpx_status, false) != STATUS_SUCCESS) return STATUS_ERROR;
int i;
for (i = 0; i < eth_count; i++) {
if (eths[i].uptime) {
eths[i].uptime /= 100;// uptime - (eths[i].uptime / 100);
}
snprintf(eths[i].location, IF_LOCATION_SIZE, "%d", i);
}
struct snmp_pdu *response;
struct variable_list *vars;
int status = snmph_get_bulk(OID_IF_NAME, OID_LENGTH(OID_IF_NAME), eth_count, &response);
if (status != STATUS_SUCCESS) return status;
i = 0;
for(vars = response->variables; vars || i < eth_count; vars = vars->next_variable) {
strncpy(eths[i].name, (char *)vars->val.string, IF_NAME_SIZE > vars->val_len ? vars->val_len : IF_NAME_SIZE);
i++;
}
snmp_free_pdu(response);
/***************** Bytes (octets) *****************/
if (fill_ethernet_stats_64(O_IF_RX_BYTES_64, OID_LENGTH(O_IF_RX_BYTES_64), eth_count, &eths[0].counters.rx_bytes, false) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_64(O_IF_TX_BYTES_64, OID_LENGTH(O_IF_TX_BYTES_64), eth_count, &eths[0].counters.tx_bytes, false) != STATUS_SUCCESS) return STATUS_ERROR;
/***************** Packets *****************/
if (fill_ethernet_stats_64(O_IF_RX_MUL_PKTS_64, OID_LENGTH(O_IF_RX_MUL_PKTS_64), eth_count, &eths[0].counters.rx_packets, false) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_64(O_IF_TX_MUL_PKTS_64, OID_LENGTH(O_IF_TX_MUL_PKTS_64), eth_count, &eths[0].counters.tx_packets, false) != STATUS_SUCCESS) return STATUS_ERROR;
// "Multicast is the sum of rx+tx multicast packets"
for (i = 0; i < eth_count; i++) {
eths[i].counters.multicast = eths[i].counters.rx_packets + eths[i].counters.tx_packets;
}
// All packets is a sum (aggregate == true) of unicast, multicast and broadcast packets
if (fill_ethernet_stats_64(O_IF_RX_U_PKTS_64, OID_LENGTH(O_IF_RX_U_PKTS_64), eth_count, &eths[0].counters.rx_packets, true) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_64(O_IF_RX_BR_PKTS_64, OID_LENGTH(O_IF_RX_BR_PKTS_64), eth_count, &eths[0].counters.rx_packets, true) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_64(O_IF_TX_U_PKTS_64, OID_LENGTH(O_IF_TX_U_PKTS_64), eth_count, &eths[0].counters.tx_packets, true) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_64(O_IF_TX_BR_PKTS_64, OID_LENGTH(O_IF_TX_BR_PKTS_64), eth_count, &eths[0].counters.tx_packets, true) != STATUS_SUCCESS) return STATUS_ERROR;
/***************** Errors *****************/
if (fill_ethernet_stats_32(O_IF_RX_ERROR_PKTS, OID_LENGTH(O_IF_RX_ERROR_PKTS), eth_count, &eths[0].counters.rx_errors, false) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_32(O_IF_TX_ERROR_PKTS, OID_LENGTH(O_IF_TX_ERROR_PKTS), eth_count, &eths[0].counters.tx_errors, false) != STATUS_SUCCESS) return STATUS_ERROR;
/***************** Dropped *****************/
if (fill_ethernet_stats_32(O_IF_RX_DISCARD_PKTS, OID_LENGTH(O_IF_RX_DISCARD_PKTS), eth_count, &eths[0].counters.rx_dropped, false) != STATUS_SUCCESS) return STATUS_ERROR;
if (fill_ethernet_stats_32(O_IF_TX_DISCARD_PKTS, OID_LENGTH(O_IF_TX_DISCARD_PKTS), eth_count, &eths[0].counters.tx_dropped, false) != STATUS_SUCCESS) return STATUS_ERROR;
return STATUS_SUCCESS;
}
int get_vlans(uint16_t **vlans, int *vlan_count) {
struct snmp_pdu *response;
struct variable_list *vars;
// printf("Try to retrieve IF count...\n");
int status = snmph_get(O_IF_COUNT, OID_LENGTH(O_IF_COUNT), &response);
// printf("Retrieved: %d\n", status);
if (status != STAT_SUCCESS) {
printf("Could not retrieve interfaces count\n");
return status;
}
// printf("Interfaces: %ld\n", response->variables->val.integer[0]);
long int max_if = response->variables->val.integer[0];
status = snmph_get_bulk(O_IF_TYPE, OID_LENGTH(O_IF_TYPE), max_if, &response);
if (status != STAT_SUCCESS) {
// printf("VLANS: could not retrieve types\n");
return STATUS_ERROR;
}
*vlan_count = 0;
for(vars = response->variables; vars; vars = vars->next_variable) {
// print_variable(vars->name, vars->name_length, vars);
if (vars->val.integer[0] == IANAIFTYPE_L2VLAN || vars->val.integer[0] == IANAIFTYPE_L3IPVLAN) {
// printf("Found VLAN: %d\n", (int) vars->name[vars->name_length - 1]);
(*vlan_count)++;
}
}
(*vlans) = malloc(sizeof(uint16_t) * (*vlan_count));
int i = 0;
for(vars = response->variables; vars; vars = vars->next_variable) {
// print_variable(vars->name, vars->name_length, vars);
if (vars->val.integer[0] == IANAIFTYPE_L2VLAN || vars->val.integer[0] == IANAIFTYPE_L3IPVLAN) {
(*vlans)[i++] = (uint16_t) ((int) vars->name[vars->name_length - 1] - 1000);
}
}
return STATUS_SUCCESS;
}

View File

@@ -1,78 +0,0 @@
diff -Nuar a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt 2023-07-21 09:53:57.450424222 +0800
+++ b/CMakeLists.txt 2023-07-21 11:36:15.395258277 +0800
@@ -1,4 +1,4 @@
-#***************************************************************************
+#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
@@ -185,9 +185,9 @@
mark_as_advanced(CURL_DISABLE_HTTP_AUTH)
option(CURL_DISABLE_IMAP "disables IMAP" OFF)
mark_as_advanced(CURL_DISABLE_IMAP)
-option(CURL_DISABLE_LDAP "disables LDAP" OFF)
+option(CURL_DISABLE_LDAP "disables LDAP" ON)
mark_as_advanced(CURL_DISABLE_LDAP)
-option(CURL_DISABLE_LDAPS "disables LDAPS" OFF)
+option(CURL_DISABLE_LDAPS "disables LDAPS" ON)
mark_as_advanced(CURL_DISABLE_LDAPS)
option(CURL_DISABLE_LIBCURL_OPTION "disables --libcurl option from the curl tool" OFF)
mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION)
@@ -433,7 +433,7 @@
endif()
if(CURL_USE_OPENSSL)
- find_package(OpenSSL REQUIRED)
+ #find_package(OpenSSL REQUIRED)
set(SSL_ENABLED ON)
set(USE_OPENSSL ON)
@@ -441,7 +441,7 @@
# version of CMake. This allows our dependents to get our dependencies
# transitively.
if(NOT CMAKE_VERSION VERSION_LESS 3.4)
- list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto)
+ #list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto)
else()
list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES})
include_directories(${OPENSSL_INCLUDE_DIR})
@@ -595,7 +595,7 @@
set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP)
check_library_exists_concat(${CMAKE_LBER_LIB} ber_init HAVE_LIBLBER)
-
+
set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES})
set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory")
if(CMAKE_LDAP_INCLUDE_DIR)
@@ -1369,12 +1369,16 @@
add_subdirectory(docs)
endif()
+INCLUDE_DIRECTORIES(../openssl/include)
+FIND_LIBRARY(openssl ssl ../openssl)
+
add_subdirectory(lib)
if(BUILD_CURL_EXE)
add_subdirectory(src)
endif()
+
cmake_dependent_option(BUILD_TESTING "Build tests"
ON "PERL_FOUND;NOT CURL_DISABLE_TESTS"
OFF)
diff -Nuar a/src/CMakeLists.txt b/src/CMakeLists.txt
--- a/src/CMakeLists.txt 2023-07-21 13:47:10.160906907 +0800
+++ b/src/CMakeLists.txt 2023-07-21 13:49:45.205682320 +0800
@@ -98,6 +98,9 @@
#Build curl executable
target_link_libraries(${EXE_NAME} libcurl ${CURL_LIBS})
+target_link_libraries(${EXE_NAME} -lssl)
+target_link_libraries(${EXE_NAME} -lcrypto)
+target_link_libraries(${EXE_NAME} ${CMAKE_SHARED_LINKER_FLAGS})
################################################################################

View File

@@ -1,14 +0,0 @@
--- a/CMakeLists.txt 2020-10-26 04:31:31.000000000 -0700
+++ b/CMakeLists.txt 2023-04-10 20:15:13.399705011 -0700
@@ -102,8 +102,9 @@
# ideally we want to use pipe2()
-
-CHECK_C_SOURCE_COMPILES("#define _GNU_SOURCE\n#include <unistd.h>\nint main(void) {int fd[2];\n return pipe2(fd, 0);\n}\n" LWS_HAVE_PIPE2)
+# jacky
+# comment out this line, use pipe() instead of pipe2()
+#CHECK_C_SOURCE_COMPILES("#define _GNU_SOURCE\n#include <unistd.h>\nint main(void) {int fd[2];\n return pipe2(fd, 0);\n}\n" LWS_HAVE_PIPE2)
# tcp keepalive needs this on linux to work practically... but it only exists
# after kernel 2.6.37

View File

@@ -1,49 +0,0 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(ucentral-client C)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--copy-dt-needed-entries")
SET(LDFLAGS -fopenmp -Wl,--copy-dt-needed-entries)
INCLUDE_DIRECTORIES(src/include)
INCLUDE_DIRECTORIES(../)
INCLUDE_DIRECTORIES(../curl/include)
INCLUDE_DIRECTORIES(../libwebsockets/include)
INCLUDE_DIRECTORIES(../openssl/include)
INCLUDE_DIRECTORIES(ucentral-client/include)
INCLUDE_DIRECTORIES(ucentral-client)
INCLUDE_DIRECTORIES(src/include)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}/../ecapi/include)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude/mibconstants)
INCLUDE_DIRECTORIES($ENV{SOURCE_PATH}/sysinclude/oem/$ENV{PROJECT_NAME})
INCLUDE_DIRECTORIES($ENV{PROJECT_PATH}/user/thirdpty/lua/net-snmp-5.4.4/include)
add_definitions(-DPLAT_EC)
if ($ENV{D_MODEL_NAME} STREQUAL ECS4130_AC5)
add_definitions(-DENDIANNESS_ADJUST)
add_definitions(-DNOT_SUPPORT_CAP_2500)
add_definitions(-DNOT_SUPPORT_NTP_DOMAIN_NAME)
add_definitions(-DSYSTEM_OID="1.3.6.1.4.1.259.10.1.55.")
elseif ($ENV{D_MODEL_NAME} STREQUAL ECS4125_10P)
add_definitions(-DSYSTEM_OID="1.3.6.1.4.1.259.10.1.57.")
else()
message(FATAL_ERROR "not support $ENV{D_MODEL_NAME}")
endif()
INCLUDE(ucentral-client/CMakeLists.txt)
INCLUDE(ucentral-client/platform/ec/CMakeLists.txt)
FIND_LIBRARY(cjson cjson ../cjson)
FIND_LIBRARY(curl curl ../curl/lib)
FIND_LIBRARY(openssl ssl ../openssl)
FIND_LIBRARY(websockets websockets ../libwebsockets/lib)
FIND_LIBRARY(crypto crypto ../openssl)
FIND_LIBRARY(ecapi_library ecapi ../ecapi/build)
FIND_LIBRARY(netsnmp_library netsnmp $ENV{PROJECT_PATH}/user/thirdpty/lua/net-snmp-5.4.4/snmplib/.libs)
ADD_EXECUTABLE(ucentral-client ${UC_SOURCES} ${PLAT_SOURCES})
TARGET_LINK_LIBRARIES(ucentral-client ${cjson} ${curl} ${openssl} ${crypto} ${websockets} ${netsnmp_library} ${ecapi_library})

View File

@@ -1,9 +0,0 @@
list(APPEND UC_SOURCES
${CMAKE_CURRENT_LIST_DIR}/proto.c
${CMAKE_CURRENT_LIST_DIR}/router-utils.c
${CMAKE_CURRENT_LIST_DIR}/ucentral-json-parser.c
${CMAKE_CURRENT_LIST_DIR}/ucentral-log.c
${CMAKE_CURRENT_LIST_DIR}/ucentral-client.c
${CMAKE_CURRENT_LIST_DIR}/inet_net_pton.c
)

View File

@@ -1,200 +0,0 @@
/*
* Copyright (c) 1996,1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef SPRINTF_CHAR
# define SPRINTF(x) strlen(sprintf/**/x)
#else
# define SPRINTF(x) ((size_t)sprintf x)
#endif
static int inet_net_pton_ipv4 (const char *src, u_char *dst,
size_t size) __THROW;
# define __rawmemchr strchr
/*
* static int
* inet_net_pton(af, src, dst, size)
* convert network number from presentation to network format.
* accepts hex octets, hex strings, decimal octets, and /CIDR.
* "size" is in bytes and describes "dst".
* return:
* number of bits, either imputed classfully or specified with /CIDR,
* or -1 if some failure occurred (check errno). ENOENT means it was
* not a valid network specification.
* author:
* Paul Vixie (ISC), June 1996
*/
int
inet_net_pton (int af, const char *src, void *dst, size_t size)
{
switch (af) {
case AF_INET:
return (inet_net_pton_ipv4(src, dst, size));
default:
//__set_errno (EAFNOSUPPORT);
return (-1);
}
}
/*
* static int
* inet_net_pton_ipv4(src, dst, size)
* convert IPv4 network number from presentation to network format.
* accepts hex octets, hex strings, decimal octets, and /CIDR.
* "size" is in bytes and describes "dst".
* return:
* number of bits, either imputed classfully or specified with /CIDR,
* or -1 if some failure occurred (check errno). ENOENT means it was
* not an IPv4 network specification.
* note:
* network byte order assumed. this means 192.5.5.240/28 has
* 0b11110000 in its fourth octet.
* author:
* Paul Vixie (ISC), June 1996
*/
static int
inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size)
{
static const char xdigits[] = "0123456789abcdef";
int n, ch, tmp, dirty, bits;
const u_char *odst = dst;
ch = *src++;
if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
&& isascii(src[1]) && isxdigit(src[1])) {
/* Hexadecimal: Eat nybble string. */
if (size <= 0)
goto emsgsize;
dirty = 0;
tmp = 0; /* To calm down gcc. */
src++; /* skip x or X. */
while (isxdigit((ch = *src++))) {
ch = _tolower(ch);
n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
assert(n >= 0 && n <= 15);
if (dirty == 0)
tmp = n;
else
tmp = (tmp << 4) | n;
if (++dirty == 2) {
if (size-- <= 0)
goto emsgsize;
*dst++ = (u_char) tmp;
dirty = 0;
}
}
if (dirty) { /* Odd trailing nybble? */
if (size-- <= 0)
goto emsgsize;
*dst++ = (u_char) (tmp << 4);
}
} else if (isascii(ch) && isdigit(ch)) {
/* Decimal: eat dotted digit string. */
for (;;) {
tmp = 0;
do {
n = ((const char *) __rawmemchr(xdigits, ch)
- xdigits);
assert(n >= 0 && n <= 9);
tmp *= 10;
tmp += n;
if (tmp > 255)
goto enoent;
} while (isascii((ch = *src++)) && isdigit(ch));
if (size-- <= 0)
goto emsgsize;
*dst++ = (u_char) tmp;
if (ch == '\0' || ch == '/')
break;
if (ch != '.')
goto enoent;
ch = *src++;
if (!isascii(ch) || !isdigit(ch))
goto enoent;
}
} else
goto enoent;
bits = -1;
if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
/* CIDR width specifier. Nothing can follow it. */
ch = *src++; /* Skip over the /. */
bits = 0;
do {
n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
assert(n >= 0 && n <= 9);
bits *= 10;
bits += n;
} while (isascii((ch = *src++)) && isdigit(ch));
if (ch != '\0')
goto enoent;
if (bits > 32)
goto emsgsize;
}
/* Firey death and destruction unless we prefetched EOS. */
if (ch != '\0')
goto enoent;
/* If nothing was written to the destination, we found no address. */
if (dst == odst)
goto enoent;
/* If no CIDR spec was given, infer width from net class. */
if (bits == -1) {
if (*odst >= 240) /* Class E */
bits = 32;
else if (*odst >= 224) /* Class D */
bits = 4;
else if (*odst >= 192) /* Class C */
bits = 24;
else if (*odst >= 128) /* Class B */
bits = 16;
else /* Class A */
bits = 8;
/* If imputed mask is narrower than specified octets, widen. */
if (bits >= 8 && bits < ((dst - odst) * 8))
bits = (dst - odst) * 8;
}
/* Extend network to cover the actual mask. */
while (bits > ((dst - odst) * 8)) {
if (size-- <= 0)
goto emsgsize;
*dst++ = '\0';
}
return (bits);
enoent:
//__set_errno (ENOENT);
return (-1);
emsgsize:
//__set_errno (EMSGSIZE);
return (-1);
}

View File

@@ -4,3 +4,8 @@ ntp server 1.pool.ntp.org prefer true
ntp server 2.pool.ntp.org prefer true
ntp server 3.pool.ntp.org prefer true
ntp authenticate
ip dhcp snooping
ip dhcp snooping Vlan1
ntp source-interface Vlan 1
interface range Ethernet 0-100
no ip dhcp snooping trust

View File

@@ -0,0 +1,2 @@
configure terminal
no ip vrf mgmt

View File

@@ -11,6 +11,7 @@ start() {
fi
cp /usr/local/lib/OLS_NOS_fixups.script /home/admin/OLS_NOS_fixups.script
cp /usr/local/lib/OLS_NOS_upgrade_override.script /home/admin/OLS_NOS_upgrade_override.script
if [ $(systemctl is-active config-setup.service) == "active" ]; then
# do nothing on service restart
@@ -29,11 +30,23 @@ start() {
}
wait() {
test -d /var/lib/ucentral || mkdir /var/lib/ucentral
# Wait for at least one Vlan to be created - a signal that telemetry is up.
# Even if vlan table is empty, private 3967 will be allocated with all
# ports in it.
while ! ls /sys/class/net/Vlan* &>/dev/null; do sleep 1; done
# Detect first boot on this version
# Run upgrade overrides before fixups
conf_upgrade_md5sum=$(md5sum /home/admin/OLS_NOS_upgrade_override.script | cut -d ' ' -f1)
if test "$conf_upgrade_md5sum" != "$(test -f /var/lib/ucentral/upgrade-override.md5sum && cat /var/lib/ucentral/upgrade-override.md5sum)"; then
sudo -u admin -- bash "sonic-cli" "/home/admin/OLS_NOS_upgrade_override.script"
echo -n "$conf_upgrade_md5sum" >/var/lib/ucentral/upgrade-override.md5sum
fi
sudo touch /etc/default/in-band-dhcp
# Temporary NTP fixup / WA: configure a list of default NTP servers.
# Should mature into a default-config option to make sure board has right
# time upon any boot (especially first time).
@@ -48,6 +61,8 @@ wait() {
# NOTE: alternatively we could use ifplugd. This also handle del/add scenario
ifup Vlan1 || true
config vlan dhcp 1 enable
# There's an issue with containers starting before DNS server is configured:
# resolf.conf file get copied from host to container upon container start.
# This means, that if resolf.conf gets altered (on host) after container's been
@@ -63,9 +78,19 @@ wait() {
# This also means, that we won't start up untill this URI is accessible.
while ! curl clientauth.one.digicert.com &>/dev/null; do sleep 1; done
# Enable DHCP trusting for uplink (Vlan1) iface
# It's needed to forward DHCP Discover (and replies) from/to DHCP server
# of (untrusted) port clients (EthernetX) of the same Vlan (Vlan1).
# Without this fix underlying Vlan members wouldn't be able to receive
# DHCP-lease IP
trusted_dhcp_if=`sudo -u admin -- bash "sonic-cli" "-c" "show ip arp" | grep -Eo "Ethernet[0-9]+"`
sudo -u admin -- "echo" "configure terminal" > /home/admin/fixup_scr.script
sudo -u admin -- "echo" "interface $trusted_dhcp_if" >> /home/admin/fixup_scr.script
sudo -u admin -- "echo" "ip dhcp snooping trust" >> /home/admin/fixup_scr.script
sudo -u admin -- bash "sonic-cli" "/home/admin/fixup_scr.script"
# change admin password
# NOTE: This could lead to access escalation, if you got image from running device
test -d /var/lib/ucentral || mkdir /var/lib/ucentral
if ! test -f /var/lib/ucentral/admin-cred.changed; then
#ADMIN_PASSWD=`openssl rand -hex 10`
ADMIN_PASSWD=broadcom

View File

@@ -154,6 +154,11 @@
"vlanid": "1"
}
},
"VLAN_INTERFACE": {
"Vlan1": {
"dhcp": "enable"
}
},
"VLAN_MEMBER": {
{% for port in PORT %}
"Vlan1|{{port}}": {
@@ -164,11 +169,6 @@
"INTERFACE": {
"Vlan1": {}
},
"MGMT_VRF_CONFIG": {
"vrf_global": {
"mgmtVrfEnabled": "true"
}
},
"VRF": {
"default": {
"enabled": "true"

View File

@@ -19,7 +19,7 @@ platform/plat.a:
ucentral-client: ucentral-client.o proto.o platform/plat.a \
ucentral-json-parser.o ucentral-log.o router-utils.o base64.o
g++ -o $@ $^ -lcurl -lwebsockets -lcjson -lssl -lcrypto -lpthread -ljsoncpp -lresolv
g++ -g -o $@ $^ -lcurl -lwebsockets -lcjson -lssl -lcrypto -lpthread -ljsoncpp -lresolv -lhiredis
test:
@echo "========= running unit tests ========="

View File

@@ -1,12 +1,19 @@
#ifndef ROUTER_UTILS_H
#define ROUTER_UTILS_H
/* Defines router types and utils for them (lookup/etc) */
#include <netinet/in.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ucentral_router_fib_key {
/* TODO vrf */
struct in_addr prefix;
int prefix_len;
} key;
};
struct ucentral_router_fib_info { /* Destination info */
enum {
@@ -46,16 +53,16 @@ struct ucentral_router {
struct ucentral_router_fib_db_apply_args {
/* plat whould check info to determine if node channged */
int (*upd_cb)(const struct ucentral_router_fib_node *old,
int (*upd_cb)(const struct ucentral_router_fib_node *old_node,
int olen,
const struct ucentral_router_fib_node *new,
const struct ucentral_router_fib_node *new_node,
int nlen,
void *arg);
/* prefix = new, info = new */
int (*add_cb)(const struct ucentral_router_fib_node *new,
int (*add_cb)(const struct ucentral_router_fib_node *new_node,
int len, void *arg);
/* prefix = none */
int (*del_cb)(const struct ucentral_router_fib_node *old,
int (*del_cb)(const struct ucentral_router_fib_node *old_node,
int len, void *arg);
void *arg;
};
@@ -69,26 +76,32 @@ int ucentral_router_fib_db_append(struct ucentral_router *r,
struct ucentral_router_fib_node *n);
int ucentral_router_fib_key_cmp(const struct ucentral_router_fib_key *a,
const struct ucentral_router_fib_key *b);
bool ucentral_router_fib_info_cmp(const struct ucentral_router_fib_info *a,
const struct ucentral_router_fib_info *b);
int ucentral_router_fib_info_cmp(const struct ucentral_router_fib_info *a,
const struct ucentral_router_fib_info *b);
#define router_db_get(R, I) (I < (R)->len ? &(R)->arr[(I)] : NULL)
#define for_router_db_diff_CASE_UPD(DIFF) if (!(DIFF))
#define for_router_db_diff_CASE_DEL(DIFF) if ((DIFF) > 0)
#define for_router_db_diff_CASE_ADD(DIFF) if ((DIFF) < 0)
#define diff_case_upd(DIFF) (!(DIFF))
#define diff_case_del(DIFF) ((DIFF) > 0)
#define diff_case_add(DIFF) ((DIFF) < 0)
#define router_db_diff_get(NEW, OLD, INEW, IOLD) \
(IOLD) == (OLD)->len \
? -1 \
: (INEW) == (NEW)->len \
? 1 \
: ucentral_router_fib_key_cmp(&(NEW)->arr[(INEW)].key, &(OLD)->arr[(IOLD)].key)
#define for_router_db_diff(NEW, OLD, INEW, IOLD, DIFF) \
for ((INEW) = 0, (IOLD) = 0, (NEW)->sorted ? 0 : ucentral_router_fib_db_sort((NEW)), (OLD)->sorted ? 0 : ucentral_router_fib_db_sort((OLD)); \
((IOLD) != (OLD)->len || (INEW) != (NEW)->len) && \
(( \
(DIFF) = (IOLD) == (OLD)->len ? -1 : (INEW) == (NEW)->len ? 1 : ucentral_router_fib_key_cmp(&(NEW)->arr[(INEW)].key, &(OLD)->arr[(IOLD)].key) \
) || 1); \
(DIFF) == 0 ? ++(INEW) && ++(IOLD) : 0, (DIFF) > 0 ? ++(IOLD) : 0, (DIFF) < 0 ? ++(INEW) : 0\
for ((INEW) = 0, (IOLD) = 0, (DIFF) = 0; \
\
((IOLD) != (OLD)->len || (INEW) != (NEW)->len); \
\
(DIFF) == 0 ? ++(INEW) && ++(IOLD) : 0, \
(DIFF) > 0 ? ++(IOLD) : 0, \
(DIFF) < 0 ? ++(INEW) : 0 \
)
/*
* ((DIFF) == 0 && ++(INEW) && ++(IOLD)) || \
* ((DIFF) > 0 && ++(IOLD)) || \
* ((DIFF) < 0 && ++(INEW)) \
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -3,6 +3,10 @@
#include <syslog.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef UC_LOG_COMPONENT
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_UNKNOWN
#endif
@@ -28,37 +32,6 @@ void uc_log_send_cb_register(void (*cb)(const char *, int sv));
void uc_log_severity_set(enum uc_log_component c, int sv);
void uc_log(enum uc_log_component c, int sv, const char *fmt, ...);
#ifdef PLAT_EC
#define UC_LOG_INFO(...) \
do { \
syslog(LOG_INFO, __VA_ARGS__); \
uc_log(UC_LOG_COMPONENT, UC_LOG_SV_INFO, __VA_ARGS__); \
} while (0)
#define UC_LOG_DBG(FMT, ...) \
do { \
syslog(LOG_DEBUG, "%s:%u: " FMT, __func__, \
(unsigned)__LINE__, ##__VA_ARGS__); \
uc_log(UC_LOG_COMPONENT, UC_LOG_SV_DEBUG, \
FMT, ##__VA_ARGS__); \
} while (0)
#define UC_LOG_ERR(FMT, ...) \
do { \
syslog(LOG_ERR, "%s:%u: " FMT, __func__, \
(unsigned)__LINE__, ##__VA_ARGS__); \
uc_log(UC_LOG_COMPONENT, UC_LOG_SV_ERR, \
FMT, ##__VA_ARGS__); \
} while (0)
#define UC_LOG_CRIT(FMT, ...) \
do { \
syslog(LOG_CRIT, "%s:%u: " FMT, __func__, \
(unsigned)__LINE__, ##__VA_ARGS__); \
uc_log(UC_LOG_COMPONENT, UC_LOG_SV_CRIT, \
FMT, ##__VA_ARGS__); \
} while (0)
#else
#define UC_LOG_INFO(...) \
do { \
syslog(LOG_INFO, __VA_ARGS__); \
@@ -88,6 +61,9 @@ void uc_log(enum uc_log_component c, int sv, const char *fmt, ...);
uc_log(UC_LOG_COMPONENT, UC_LOG_SV_CRIT, \
FMT __VA_OPT__(, ) __VA_ARGS__); \
} while (0)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,3 +1,6 @@
#ifndef UCENTRAL_PLATFORM_H
#define UCENTRAL_PLATFORM_H
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
@@ -22,18 +25,18 @@ extern "C" {
#define MAX_NUM_OF_PORTS (100)
#define PORT_MAX_NAME_LEN (32)
#ifdef PLAT_EC
#define VLAN_MAX_NAME_LEN PORT_MAX_NAME_LEN
#endif
#define RTTY_CFG_FIELD_STR_MAX_LEN (64)
#define PLATFORM_INFO_STR_MAX_LEN (96)
#define SYSLOG_CFG_FIELD_STR_MAX_LEN (64)
#define NTP_CFG_HOSTNAME_STR_MAX_LEN (64)
#define RADIUS_CFG_HOSTNAME_STR_MAX_LEN (64)
#define RADIUS_CFG_PASSKEY_STR_MAX_LEN (64)
#define RADIUS_CFG_DEFAULT_PORT (1812)
#define RADIUS_CFG_DEFAULT_PRIO (1)
#define HEALTHCHEK_MESSAGE_MAX_COUNT (10)
#define HEALTHCHEK_MESSAGE_MAX_LEN (100)
#define PLATFORM_MAC_STR_SIZE (18)
#define METRICS_WIRED_CLIENTS_MAX_NUM (2000)
/*
* TODO(vb) likely we need to parse interfaces in proto to understand
@@ -42,6 +45,8 @@ extern "C" {
*/
#define PID_TO_NAME(p, name) sprintf(name, "Ethernet%hu", p)
#define NAME_TO_PID(p, name) sscanf((name), "Ethernet%hu", (p))
#define VLAN_TO_NAME(v, name) sprintf((name), "Vlan%hu", (v))
#define NAME_TO_VLAN(v, name) sscanf((name), "Vlan%hu", (v))
struct plat_vlan_memberlist;
struct plat_port_vlan;
@@ -65,6 +70,18 @@ enum plat_ieee8021x_port_host_mode {
PLAT_802_1X_PORT_HOST_MODE_SINGLE_HOST,
};
enum plat_ieee8021x_das_auth_type {
PLAT_802_1X_DAS_AUTH_TYPE_ANY,
PLAT_802_1X_DAS_AUTH_TYPE_ALL,
PLAT_802_1X_DAS_AUTH_TYPE_SESSION_KEY,
};
enum plat_igmp_version {
PLAT_IGMP_VERSION_1,
PLAT_IGMP_VERSION_2,
PLAT_IGMP_VERSION_3
};
#define UCENTRAL_PORT_LLDP_PEER_INFO_MAX_MGMT_IPS (2)
/* Interface LLDP peer's data, as defined in interface.lldp.yml*/
struct plat_port_lldp_peer_info {
@@ -78,7 +95,7 @@ struct plat_port_lldp_peer_info {
/* The chassis name that our neighbour is announcing */
char name[64];
/* The chassis MAC that our neighbour is announcing */
char mac[18];
char mac[PLATFORM_MAC_STR_SIZE];
/* The chassis description that our neighbour is announcing */
char description[512];
/* The management IPs that our neighbour is announcing */
@@ -116,7 +133,7 @@ struct plat_poe_port_state {
struct plat_ieee8021x_authenticated_client_info {
char auth_method[32];
char mac_addr[18];
char mac_addr[PLATFORM_MAC_STR_SIZE];
size_t session_time;
char username[64];
char vlan_type[32];
@@ -253,15 +270,29 @@ struct plat_port_l2 {
struct plat_ipv4 ipv4;
};
struct plat_igmp {
bool exist;
bool snooping_enabled;
bool querier_enabled;
bool fast_leave_enabled;
uint32_t query_interval;
uint32_t last_member_query_interval;
uint32_t max_response_time;
enum plat_igmp_version version;
size_t num_groups;
struct {
struct in_addr addr;
struct plat_ports_list *egress_ports_list;
} *groups;
};
struct plat_port_vlan {
struct plat_vlan_memberlist *members_list_head;
struct plat_ipv4 ipv4;
struct plat_dhcp dhcp;
struct plat_igmp igmp;
uint16_t id;
uint16_t mstp_instance;
#ifdef PLAT_EC
char name[VLAN_MAX_NAME_LEN];
#endif
};
struct plat_vlans_list {
@@ -275,9 +306,6 @@ struct plat_vlan_memberlist {
uint16_t fp_id;
} port;
bool tagged;
#ifdef PLAT_EC
bool pvid;
#endif
struct plat_vlan_memberlist *next;
};
@@ -289,6 +317,27 @@ struct plat_syslog_cfg {
char host[SYSLOG_CFG_FIELD_STR_MAX_LEN];
};
struct plat_enabled_service_cfg {
struct {
bool enabled;
} ssh;
struct telnet {
bool enabled;
} telnet;
struct {
bool enabled;
} http;
};
struct plat_ntp_server {
char hostname[NTP_CFG_HOSTNAME_STR_MAX_LEN];
struct plat_ntp_server *next;
};
struct plat_ntp_cfg {
struct plat_ntp_server *servers;
};
struct plat_rtty_cfg {
char id[RTTY_CFG_FIELD_STR_MAX_LEN];
char passwd[RTTY_CFG_FIELD_STR_MAX_LEN];
@@ -331,6 +380,7 @@ struct plat_metrics_cfg {
int lldp_enabled;
int clients_enabled;
size_t interval;
unsigned max_mac_count;
/* IE GET max length. Should be enoug. */
char public_ip_lookup[2048];
} state;
@@ -343,8 +393,16 @@ struct plat_unit_poe_cfg {
bool is_usage_threshold_set;
};
struct plat_unit_system_cfg {
char password[64];
bool password_changed;
};
struct plat_unit {
struct plat_unit_poe_cfg poe;
struct plat_unit_system_cfg system;
bool mc_flood_control;
bool querier_enable;
};
enum plat_stp_mode {
@@ -376,6 +434,31 @@ struct plat_radius_hosts_list {
struct plat_radius_host host;
};
struct plat_ieee8021x_dac_host {
char hostname[RADIUS_CFG_HOSTNAME_STR_MAX_LEN];
char passkey[RADIUS_CFG_PASSKEY_STR_MAX_LEN];
};
struct plat_ieee8021x_dac_list {
struct plat_ieee8021x_dac_list *next;
struct plat_ieee8021x_dac_host host;
};
struct plat_port_isolation_session_ports {
struct plat_ports_list *ports_list;
};
struct plat_port_isolation_session {
uint64_t id;
struct plat_port_isolation_session_ports uplink;
struct plat_port_isolation_session_ports downlink;
};
struct plat_port_isolation_cfg {
struct plat_port_isolation_session *sessions;
size_t sessions_num;
};
struct plat_cfg {
struct plat_unit unit;
/* Alloc all ports, but access them only if bit is set. */
@@ -385,6 +468,8 @@ struct plat_cfg {
BITMAP_DECLARE(vlans_to_cfg, MAX_VLANS);
struct plat_metrics_cfg metrics;
struct plat_syslog_cfg *log_cfg;
struct plat_enabled_service_cfg enabled_services_cfg;
struct plat_ntp_cfg ntp_cfg;
/* Port's interfaces (provide l2 iface w/o bridge caps) */
struct plat_port_l2 portsl2[MAX_NUM_OF_PORTS];
struct ucentral_router router;
@@ -393,9 +478,25 @@ struct plat_cfg {
/* Instance zero is for global instance (like common values in rstp) */
struct plat_stp_instance_cfg stp_instances[MAX_VLANS];
struct plat_radius_hosts_list *radius_hosts_list;
bool ieee8021x_is_auth_ctrl_enabled;
struct {
bool is_auth_ctrl_enabled;
bool bounce_port_ignore;
bool disable_port_ignore;
bool ignore_server_key;
bool ignore_session_key;
char server_key[RADIUS_CFG_PASSKEY_STR_MAX_LEN];
enum plat_ieee8021x_das_auth_type das_auth_type;
struct plat_ieee8021x_dac_list *das_dac_list;
} ieee8021x;
struct plat_port_isolation_cfg port_isolation_cfg;
bool jumbo_frames;
};
struct plat_learned_mac_addr {
char port[PORT_MAX_NAME_LEN];
int vid;
char mac[PLATFORM_MAC_STR_SIZE];
};
typedef void (*plat_alarm_cb)(struct plat_alarm *);
@@ -457,9 +558,6 @@ typedef void (*plat_run_script_cb)(int err, struct plat_run_script_result *,
void *ctx);
enum {
#ifdef PLAT_EC
UCENTRAL_PORT_SPEED_NONE,
#endif
UCENTRAL_PORT_SPEED_10_E,
UCENTRAL_PORT_SPEED_100_E,
UCENTRAL_PORT_SPEED_1000_E,
@@ -472,9 +570,6 @@ enum {
};
enum {
#ifdef PLAT_EC
UCENTRAL_PORT_DUPLEX_NONE,
#endif
UCENTRAL_PORT_DUPLEX_HALF_E,
UCENTRAL_PORT_DUPLEX_FULL_E,
};
@@ -501,17 +596,60 @@ enum {
PLAT_REBOOT_CAUSE_REBOOT_CMD,
PLAT_REBOOT_CAUSE_POWERLOSS,
PLAT_REBOOT_CAUSE_CRASH,
PLAT_REBOOT_CAUSE_UNAVAILABLE,
};
enum sfp_form_factor {
UCENTRAL_SFP_FORM_FACTOR_NA = 0,
UCENTRAL_SFP_FORM_FACTOR_SFP,
UCENTRAL_SFP_FORM_FACTOR_SFP_PLUS,
UCENTRAL_SFP_FORM_FACTOR_SFP_28,
UCENTRAL_SFP_FORM_FACTOR_SFP_DD,
UCENTRAL_SFP_FORM_FACTOR_QSFP,
UCENTRAL_SFP_FORM_FACTOR_QSFP_PLUS,
UCENTRAL_SFP_FORM_FACTOR_QSFP_28,
UCENTRAL_SFP_FORM_FACTOR_QSFP_DD
};
enum sfp_link_mode {
UCENTRAL_SFP_LINK_MODE_NA = 0,
UCENTRAL_SFP_LINK_MODE_1000_X,
UCENTRAL_SFP_LINK_MODE_2500_X,
UCENTRAL_SFP_LINK_MODE_4000_SR,
UCENTRAL_SFP_LINK_MODE_10G_SR,
UCENTRAL_SFP_LINK_MODE_25G_SR,
UCENTRAL_SFP_LINK_MODE_40G_SR,
UCENTRAL_SFP_LINK_MODE_50G_SR,
UCENTRAL_SFP_LINK_MODE_100G_SR,
};
struct plat_port_transceiver_info {
char vendor_name[64];
char part_number[64];
char serial_number[64];
char revision[64];
enum sfp_form_factor form_factor;
enum sfp_link_mode *supported_link_modes;
size_t num_supported_link_modes;
float temperature;
float tx_optical_power;
float rx_optical_power;
float max_module_power;
};
struct plat_port_info {
struct plat_port_counters stats;
struct plat_port_lldp_peer_info lldp_peer_info;
struct plat_ieee8021x_port_info ieee8021x_info;
struct plat_port_transceiver_info transceiver_info;
uint32_t uptime;
uint32_t speed;
uint8_t carrier_up;
uint8_t duplex;
uint8_t has_lldp_peer_info;
uint8_t has_transceiver_info;
char name[PORT_MAX_NAME_LEN];
};
@@ -525,6 +663,24 @@ struct plat_system_info {
double load_average[3]; /* 1, 5, 15 minutes load average */
};
struct plat_iee8021x_coa_counters {
uint64_t coa_req_received;
uint64_t coa_ack_sent;
uint64_t coa_nak_sent;
uint64_t coa_ignored;
uint64_t coa_wrong_attr;
uint64_t coa_wrong_attr_value;
uint64_t coa_wrong_session_context;
uint64_t coa_administratively_prohibited_req;
};
struct plat_gw_address {
struct in_addr ip;
uint32_t metric;
char port[PORT_MAX_NAME_LEN];
char mac[PLATFORM_MAC_STR_SIZE];
};
struct plat_state_info {
struct plat_poe_state poe_state;
struct plat_poe_port_state poe_ports_state[MAX_NUM_OF_PORTS];
@@ -532,8 +688,15 @@ struct plat_state_info {
struct plat_port_info *port_info;
int port_info_count;
struct plat_port_vlan *vlan_info;
size_t vlan_info_count;
struct plat_learned_mac_addr *learned_mac_list;
size_t learned_mac_list_size;
struct plat_gw_address *gw_addr_list;
size_t gw_addr_list_size;
struct plat_system_info system_info;
struct plat_iee8021x_coa_counters ieee8021x_global_coa_counters;
};
struct plat_upgrade_info {
@@ -559,7 +722,14 @@ struct plat_event_callbacks {
plat_poe_link_faultcode_cb poe_link_faultcode_cb;
};
enum plat_script_type {
PLAT_SCRIPT_TYPE_NA = 0,
PLAT_SCRIPT_TYPE_SHELL = 1,
PLAT_SCRIPT_TYPE_DIAGNOSTICS = 2,
};
struct plat_run_script_result {
enum plat_script_type type;
const char *stdout_string;
size_t stdout_string_len;
int exit_status;
@@ -567,7 +737,7 @@ struct plat_run_script_result {
};
struct plat_run_script {
const char *type;
enum plat_script_type type;
const char *script_base64;
plat_run_script_cb cb;
void *ctx;
@@ -586,11 +756,7 @@ int plat_metrics_save(const struct plat_metrics_cfg *cfg);
int plat_metrics_restore(struct plat_metrics_cfg *cfg);
int plat_saved_config_id_get(uint64_t *id);
void plat_config_destroy(struct plat_cfg *cfg);
#ifdef PLAT_EC
int plat_factory_default(bool keep_redirector);
#else
int plat_factory_default(void);
#endif
int plat_rtty(struct plat_rtty_cfg *rtty_cfg);
int plat_upgrade(char *uri, char *signature);
@@ -621,15 +787,12 @@ int plat_run_script(struct plat_run_script *);
int plat_port_list_get(uint16_t list_size, struct plat_ports_list *ports);
int plat_port_num_get(uint16_t *num_of_active_ports);
int plat_running_img_name_get(char *str, size_t str_max_len);
int plat_revision_get(char *str, size_t str_max_len);
int
plat_reboot_cause_get(struct plat_reboot_cause *cause);
int plat_diagnostic(char *res_path);
#ifdef PLAT_EC
void clean_stats();
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,12 +1,19 @@
plat.a: plat.o
ar crs $@ $^
plat.o: plat-gnma.o gnma/gnma.full.a
plat.o: plat-gnma.o gnma/gnma.full.a netlink/netlink.full.a
# TODO(vb) get back to this
gcc -r -nostdlib -o $@ $^
gnma/gnma.full.a:
$(MAKE) -C $(dir $@) $(notdir $@)
netlink/netlink.full.a:
$(MAKE) -C $(dir $@) $(notdir $@)
%.o: %.c
ifdef PLATFORM_REVISION
gcc -c -o $@ ${CFLAGS} -I ./ -I ../../include -D PLATFORM_REVISION='"$(PLATFORM_REVISION)"' $^
else
gcc -c -o $@ ${CFLAGS} -I ./ -I ../../include $^
endif

View File

@@ -1,7 +1,7 @@
all: gnma.a
%.o: %.c
gcc -c -o $@ ${CFLAGS} -I ./ -I../../../include $<
gcc -c -o $@ ${CFLAGS} -I ./ -I../../../include -I../netlink $<
gnma.a: gnma_common.o
ar crs $@ $^

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@
#define GNMA_RADIUS_CFG_HOSTNAME_STR_MAX_LEN (64)
#define GNMA_RADIUS_CFG_PASSKEY_STR_MAX_LEN (64)
#define GNMA_OK 0
#define GNMA_ERR_COMMON -1
#define GNMA_ERR_OVERFLOW -2
@@ -26,6 +27,16 @@ struct gnma_radius_host_key {
char hostname[GNMA_RADIUS_CFG_HOSTNAME_STR_MAX_LEN];
};
struct gnma_das_dac_host_key {
char hostname[GNMA_RADIUS_CFG_HOSTNAME_STR_MAX_LEN];
};
typedef enum _gnma_das_auth_type_t {
GNMA_802_1X_DAS_AUTH_TYPE_ANY,
GNMA_802_1X_DAS_AUTH_TYPE_ALL,
GNMA_802_1X_DAS_AUTH_TYPE_SESSION_KEY,
} gnma_das_auth_type_t;
struct gnma_metadata {
char platform[GNMA_METADATA_STR_MAX_LEN];
char hwsku[GNMA_METADATA_STR_MAX_LEN];
@@ -58,6 +69,17 @@ typedef enum _gnma_port_stat_type_t {
} gnma_port_stat_type_t;
typedef enum _gnma_ieee8021x_das_dac_stat_type_t {
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_ACK_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_NAK_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_IGNORED_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_VALUE_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_SESSION_CONTEXT_PKTS,
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_ADMINISTRATIVELY_PROHIBITED_REQ_PKTS,
} gnma_ieee8021x_das_dac_stat_type_t;
struct gnma_alarm {
const char *id;
const char *resource;
@@ -129,7 +151,9 @@ struct gnma_route_attrs {
} connected;
struct {
uint16_t vid;
uint32_t metric;
struct in_addr gw;
struct gnma_port_key egress_port;
} nexthop;
};
};
@@ -256,6 +280,47 @@ struct gnma_vlan_member_bmap {
} vlan[GNMA_MAX_VLANS];
};
typedef enum _gnma_fdb_entry_type_t {
GNMA_FDB_ENTRY_TYPE_STATIC,
GNMA_FDB_ENTRY_TYPE_DYNAMIC,
} gnma_fdb_entry_type_t;
struct gnma_fdb_entry {
struct gnma_port_key port;
gnma_fdb_entry_type_t type;
int vid;
char mac[18];
};
typedef enum _gnma_igmp_version_t {
GNMA_IGMP_VERSION_NA = 0,
GNMA_IGMP_VERSION_1 = 1,
GNMA_IGMP_VERSION_2 = 2,
GNMA_IGMP_VERSION_3 = 3
} gnma_igmp_version_t;
struct gnma_igmp_snoop_attr {
bool enabled;
bool querier_enabled;
bool fast_leave_enabled;
uint32_t query_interval;
uint32_t last_member_query_interval;
uint32_t max_response_time;
gnma_igmp_version_t version;
};
struct gnma_igmp_static_group_attr {
struct in_addr address;
size_t num_ports;
struct gnma_port_key *egress_ports;
};
struct gnma_vlan_ip_t {
uint16_t vid;
uint16_t prefixlen;
struct in_addr address;
};
int gnma_switch_create(/* TODO id */ /* TODO: attr (adr, login, psw) */);
int gnma_port_admin_state_set(struct gnma_port_key *port_key, bool up);
int gnma_port_speed_set(struct gnma_port_key *port_key, const char *speed);
@@ -380,6 +445,9 @@ int gnma_route_remove(uint16_t vr_id /* 0 - default */,
int gnma_route_list_get(uint16_t vr_id, uint32_t *list_size,
struct gnma_ip_prefix *prefix_list,
struct gnma_route_attrs *attr_list);
int gnma_dyn_route_list_get(size_t *list_size,
struct gnma_ip_prefix *prefix_list,
struct gnma_route_attrs *attr_list);
int gnma_stp_mode_set(gnma_stp_mode_t mode, struct gnma_stp_attr *attr);
int gnma_stp_mode_get(gnma_stp_mode_t *mode, struct gnma_stp_attr *attr);
@@ -390,23 +458,53 @@ int gnma_stp_ports_enable(uint32_t list_size, struct gnma_port_key *ports_list);
int gnma_stp_instance_set(uint16_t instance, uint16_t prio,
uint32_t list_size, uint16_t *vid_list);
int gnma_stp_vids_enable(uint32_t list_size, uint16_t *vid_list);
int gnma_stp_vids_enable_all(void);
int gnma_stp_vids_set(uint32_t list_size, uint16_t *vid_list, bool enable);
int gnma_stp_vids_set_all(bool enable);
int gnma_stp_vid_set(uint16_t vid, struct gnma_stp_attr *attr);
int gnma_stp_vid_bulk_get(struct gnma_stp_attr *list, ssize_t size);
int gnma_ieee8021x_system_auth_control_set(bool is_enabled);
int gnma_ieee8021x_system_auth_control_get(bool *is_enabled);
int gnma_ieee8021x_system_auth_clients_get(char *buf, size_t buf_size);
int gnma_ieee8021x_das_bounce_port_ignore_set(bool bounce_port_ignore);
int gnma_ieee8021x_das_bounce_port_ignore_get(bool *bounce_port_ignore);
int gnma_ieee8021x_das_disable_port_ignore_set(bool disable_port_ignore);
int gnma_ieee8021x_das_disable_port_ignore_get(bool *disable_port_ignore);
int gnma_ieee8021x_das_ignore_server_key_set(bool ignore_server_key);
int gnma_ieee8021x_das_ignore_server_key_get(bool *ignore_server_key);
int gnma_ieee8021x_das_ignore_session_key_set(bool ignore_session_key);
int gnma_ieee8021x_das_ignore_session_key_get(bool *ignore_session_key);
int gnma_ieee8021x_das_auth_type_key_set(gnma_das_auth_type_t auth_type);
int gnma_ieee8021x_das_auth_type_key_get(gnma_das_auth_type_t *auth_type);
int gnma_ieee8021x_das_dac_hosts_list_get(size_t *list_size,
struct gnma_das_dac_host_key *das_dac_keys_arr);
int gnma_ieee8021x_das_dac_host_add(struct gnma_das_dac_host_key *key,
const char *passkey);
int gnma_ieee8021x_das_dac_host_remove(struct gnma_das_dac_host_key *key);
int
gnma_iee8021x_das_dac_global_stats_get(uint32_t num_of_counters,
gnma_ieee8021x_das_dac_stat_type_t *counter_ids,
uint64_t *counters);
int gnma_radius_hosts_list_get(size_t *list_size,
struct gnma_radius_host_key *hosts_list);
int gnma_radius_host_add(struct gnma_radius_host_key *key, const char *passkey,
uint16_t auth_port, uint8_t prio);
int gnma_radius_host_remove(struct gnma_radius_host_key *key);
int gnma_mac_address_list_get(size_t *list_size, struct gnma_fdb_entry *list);
int gnma_system_password_set(char *password);
int gnma_igmp_snooping_set(uint16_t vid, struct gnma_igmp_snoop_attr *attr);
int gnma_igmp_static_groups_set(uint16_t vid, size_t num_groups,
struct gnma_igmp_static_group_attr *groups);
int gnma_nei_addr_get(struct gnma_port_key *iface, struct in_addr *ip,
char *mac, size_t buf_size);
int gnma_igmp_iface_groups_get(struct gnma_port_key *iface,
char *buf, size_t *buf_size);
struct gnma_change *gnma_change_create(void);
void gnma_change_destory(struct gnma_change *);
int gnma_change_exec(struct gnma_change *);
int gnma_techsupport_start(char *res_path);
int gnma_ip_iface_addr_get(struct gnma_vlan_ip_t *address_list, size_t *list_size);

View File

@@ -0,0 +1,10 @@
all: netlink.a
%.o: %.c
gcc -c -o $@ ${CFLAGS} -I ./ -I/usr/include/libnl3 -lnl-3 -lnl-route-3 $<
netlink.a: netlink_common.o
ar crs $@ $^
netlink.full.a: netlink.a
ar crsT $@ $^

View File

@@ -0,0 +1,220 @@
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h>
#include <netlink/netlink.h>
#include <netlink/route/link.h>
#include <netlink/route/route.h>
#include <netlink/route/addr.h>
#include <errno.h>
#include <netlink_common.h>
#define BUFFER_SIZE 4096
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define for_each_nlmsg(n, buf, len) \
for (n = (struct nlmsghdr*)buf; \
NLMSG_OK(n, (uint32_t)len) && n->nlmsg_type != NLMSG_DONE; \
n = NLMSG_NEXT(n, len))
#define for_each_rattr(n, buf, len) \
for (n = (struct rtattr*)buf; RTA_OK(n, len); n = RTA_NEXT(n, len))
static int _nl_connect(int *sock)
{
int s;
s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (s == -1)
return -1;
*sock = s;
return 0;
}
static void _nl_disconnect(int sock)
{
close(sock);
}
static int _nl_request_ip_send(int sock)
{
struct sockaddr_nl sa = {.nl_family = AF_NETLINK};
char buf[BUFFER_SIZE];
struct ifaddrmsg *ifa;
struct nlmsghdr *nl;
struct msghdr msg;
struct iovec iov;
int res;
memset(&msg, 0, sizeof(msg));
memset(buf, 0, BUFFER_SIZE);
nl = (struct nlmsghdr*)buf;
nl->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
nl->nlmsg_type = RTM_GETADDR;
nl->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
iov.iov_base = nl;
iov.iov_len = nl->nlmsg_len;
ifa = (struct ifaddrmsg*)NLMSG_DATA(nl);
ifa->ifa_family = AF_INET; /* IPv4 */
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
res = sendmsg(sock, &msg, 0);
if (res < 0)
return -1;
return 0;
}
static int _nl_response_get(int sock, void *buf, size_t *len)
{
struct iovec iov = {.iov_base = buf, .iov_len = *len};
struct sockaddr_nl sa = {.nl_family = AF_NETLINK};
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1
};
int res;
res = recvmsg(sock, &msg, 0);
if (res < 0)
return -1;
*len = res;
return 0;
}
static int _nl_iface_addr_parse(uint32_t vid, void *buf, size_t len,
unsigned char prefixlen, struct nl_vid_addr *addr)
{
struct rtattr *rta = NULL;
for_each_rattr(rta, buf, len) {
if (rta->rta_type == IFA_LOCAL) {
memcpy(&addr->address, RTA_DATA(rta), sizeof(addr->address));
addr->vid = vid;
addr->prefixlen = prefixlen;
break;
}
}
return 0;
}
static int _nl_response_addr_parse(void *buf,
size_t len,
struct nl_vid_addr *addr_list,
size_t *list_size)
{
struct ifaddrmsg *iface_addr;
struct nlmsghdr *nl = NULL;
char ifname[IF_NAMESIZE];
size_t num_addrs = 0;
uint32_t vid;
int err = 0;
for_each_nlmsg(nl, buf, len) {
if (nl->nlmsg_type == NLMSG_ERROR)
return -1;
if (nl->nlmsg_type != RTM_NEWADDR) /* only care for addr */
continue;
iface_addr = (struct ifaddrmsg*)NLMSG_DATA(nl);
if (!if_indextoname(iface_addr->ifa_index, ifname))
return -1;
if (sscanf(ifname, "Vlan%u", &vid) != 1)
continue;
if (!addr_list || *list_size == 0) {
num_addrs++;
continue;
}
if (num_addrs > *list_size)
return -EOVERFLOW;
err = _nl_iface_addr_parse(vid, IFA_RTA(iface_addr), IFA_PAYLOAD(nl),
iface_addr->ifa_prefixlen,
&addr_list[num_addrs++]);
if (err)
break;
}
if (num_addrs > *list_size)
err = -EOVERFLOW;
*list_size = num_addrs;
if (err)
return err;
return nl->nlmsg_type == NLMSG_DONE? -ENODATA : 0;
}
int nl_get_ip_list(struct nl_vid_addr *addr_list, size_t *list_size)
{
size_t buf_len = BUFFER_SIZE, batch_size = 0, num_addrs = 0;
char buf[BUFFER_SIZE];
int sock = 0;
int err;
err = _nl_connect(&sock);
if (err)
return err;
err = _nl_request_ip_send(sock);
if (err)
goto out;
while (1) {
err = _nl_response_get(sock, buf, &buf_len);
if (err)
goto out;
err = _nl_response_addr_parse(buf, buf_len, NULL, &batch_size);
if (err == -ENODATA) {
err = 0;
break;
}
if (err && err != -EOVERFLOW) {
goto out;
}
num_addrs += batch_size;
if (!addr_list || *list_size == 0)
continue;
if (num_addrs > *list_size) {
err = -EOVERFLOW;
break;
}
err = _nl_response_addr_parse(buf, buf_len, &addr_list[num_addrs - batch_size], &batch_size);
if (unlikely(err == -ENODATA)) {
err = 0;
break;
}
if (err)
goto out;
}
if (num_addrs > *list_size)
err = -EOVERFLOW;
*list_size = num_addrs;
out:
_nl_disconnect(sock);
return err;
}

View File

@@ -0,0 +1,12 @@
#ifndef _NETLINK_COMMON
#define _NETLINK_COMMON
struct nl_vid_addr {
uint16_t vid;
uint16_t prefixlen;
uint32_t address;
};
int nl_get_ip_list(struct nl_vid_addr *addr_list, size_t *list_size);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
#ifndef _PLAT_REVISION
#define _PLAT_REVISION
#define XSTR(x) STR(x)
#define STR(x) #x
#define PLATFORM_REL_NUM 2.2
#define PLATFORM_BUILD_NUM 5
#ifndef PLATFORM_REVISION
#define PLATFORM_REVISION "Rel " XSTR(PLATFORM_REL_NUM) " build " XSTR(PLATFORM_BUILD_NUM)
#endif
#endif

View File

@@ -1,3 +0,0 @@
list(APPEND PLAT_SOURCES
${CMAKE_CURRENT_LIST_DIR}/plat-ec.c
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
---
BasedOnStyle: LLVM
IndentWidth: 4
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: false
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
ColumnLimit: 120
DerivePointerAlignment: false
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
NamespaceIndentation: Inner
PackConstructorInitializers: CurrentLine
PointerAlignment: Right
RequiresExpressionIndentation: OuterScope
UseTab: Never
...

View File

@@ -0,0 +1,118 @@
cmake_minimum_required(VERSION 3.13)
project(larch-sonic VERSION 0.1.0 LANGUAGES C CXX)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(grpc)
include(FetchContent)
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz)
FetchContent_MakeAvailable(json)
set(REDIS_PLUS_PLUS_BUILD_TEST OFF)
set(REDIS_PLUS_PLUS_BUILD_SHARED OFF)
FetchContent_Declare(redis-plus-plus URL https://github.com/sewenew/redis-plus-plus/archive/refs/tags/1.3.12.zip)
FetchContent_MakeAvailable(redis-plus-plus)
add_subdirectory(protos)
add_library(larch-sonic
STATIC
"fdb.cpp"
"igmp.cpp"
"metrics.cpp"
"plat-larch.cpp"
"port.cpp"
"route.cpp"
"sai_redis.cpp"
"services.cpp"
"state.cpp"
"stp.cpp"
"syslog.cpp"
"utils.cpp"
"vlan.cpp"
)
target_compile_features(larch-sonic PRIVATE cxx_std_17)
target_link_libraries(larch-sonic
PRIVATE
proto-objects
nlohmann_json::nlohmann_json
redis++::redis++_static
)
target_include_directories(larch-sonic
PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/../../include
)
set_target_properties(larch-sonic PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
add_custom_target(plat.a
DEPENDS larch-sonic
COMMAND ${CMAKE_AR} rcsT ${PROJECT_SOURCE_DIR}/plat.a
$<TARGET_FILE:larch-sonic>
$<TARGET_FILE:redis++::redis++_static>
/usr/local/lib/libabsl_bad_optional_access.a
/usr/local/lib/libabsl_bad_variant_access.a
/usr/local/lib/libabsl_base.a
/usr/local/lib/libabsl_city.a
/usr/local/lib/libabsl_civil_time.a
/usr/local/lib/libabsl_cord.a
/usr/local/lib/libabsl_cord_internal.a
/usr/local/lib/libabsl_cordz_functions.a
/usr/local/lib/libabsl_cordz_handle.a
/usr/local/lib/libabsl_cordz_info.a
/usr/local/lib/libabsl_debugging_internal.a
/usr/local/lib/libabsl_demangle_internal.a
/usr/local/lib/libabsl_exponential_biased.a
/usr/local/lib/libabsl_graphcycles_internal.a
/usr/local/lib/libabsl_hash.a
/usr/local/lib/libabsl_hashtablez_sampler.a
/usr/local/lib/libabsl_int128.a
/usr/local/lib/libabsl_log_severity.a
/usr/local/lib/libabsl_low_level_hash.a
/usr/local/lib/libabsl_malloc_internal.a
/usr/local/lib/libabsl_random_distributions.a
/usr/local/lib/libabsl_random_internal_platform.a
/usr/local/lib/libabsl_random_internal_pool_urbg.a
/usr/local/lib/libabsl_random_internal_randen.a
/usr/local/lib/libabsl_random_internal_randen_hwaes.a
/usr/local/lib/libabsl_random_internal_randen_hwaes_impl.a
/usr/local/lib/libabsl_random_internal_randen_slow.a
/usr/local/lib/libabsl_random_internal_seed_material.a
/usr/local/lib/libabsl_random_seed_gen_exception.a
/usr/local/lib/libabsl_random_seed_sequences.a
/usr/local/lib/libabsl_raw_hash_set.a
/usr/local/lib/libabsl_raw_logging_internal.a
/usr/local/lib/libabsl_spinlock_wait.a
/usr/local/lib/libabsl_stacktrace.a
/usr/local/lib/libabsl_status.a
/usr/local/lib/libabsl_statusor.a
/usr/local/lib/libabsl_str_format_internal.a
/usr/local/lib/libabsl_strerror.a
/usr/local/lib/libabsl_strings.a
/usr/local/lib/libabsl_strings_internal.a
/usr/local/lib/libabsl_symbolize.a
/usr/local/lib/libabsl_synchronization.a
/usr/local/lib/libabsl_throw_delegate.a
/usr/local/lib/libabsl_time.a
/usr/local/lib/libabsl_time_zone.a
/usr/local/lib/libaddress_sorting.a
/usr/local/lib/libcares.a
/usr/local/lib/libcrypto.a
/usr/local/lib/libgpr.a
/usr/local/lib/libgrpc++.a
/usr/local/lib/libgrpc++_reflection.a
/usr/local/lib/libgrpc.a
/usr/local/lib/libprotobuf.a
/usr/local/lib/libre2.a
/usr/local/lib/libssl.a
/usr/local/lib/libupb.a
/usr/local/lib/libz.a
VERBATIM
)

View File

@@ -0,0 +1,22 @@
# This is a connector between ucentral-client Makefile and larch-sonic platform CMake.
# When `plat.a` target is requested, CMake generates the underlying build system files
# (i.e. Makefile) and make is run there.
CMAKE_FILE := CMakeLists.txt
CMAKE_BUILD_DIR := build
MACHINE_ARCH := $(shell arch)
CMAKE_VERSION := 3.29.3
CMAKE_DIR := cmake-$(CMAKE_VERSION)-linux-$(MACHINE_ARCH)
CMAKE_BINARY := $(CMAKE_DIR)/bin/cmake
plat.a: $(CMAKE_BUILD_DIR)/Makefile
$(MAKE) -C $(<D) $@
# <D - directory part (i.e. path without file) of the first prerequisite
# @D - directory part of the target
$(CMAKE_BUILD_DIR)/Makefile: $(CMAKE_BINARY) $(CMAKE_FILE)
$(CMAKE_BINARY) -S $(dir $(CMAKE_FILE)) -B $(@D) -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
$(CMAKE_BINARY):
wget -qO- --no-check-certificate "https://github.com/Kitware/CMake/releases/download/v$(CMAKE_VERSION)/cmake-$(CMAKE_VERSION)-linux-$(MACHINE_ARCH).tar.gz" | tar -xz

View File

@@ -0,0 +1,14 @@
# Find Protobuf
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using Protobuf ${Protobuf_VERSION}")
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)
set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
# Find gRPC
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP gRPC::grpc++)
set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)

View File

@@ -0,0 +1,143 @@
#include <fdb.hpp>
#include <sai_redis.hpp>
#include <state.hpp>
#include <nlohmann/json.hpp>
#include <sw/redis++/redis++.h>
#include <ucentral-platform.h>
#include <cstdint>
#include <cstdio> // std::snprintf
#include <iterator> // std:inserter
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using nlohmann::json;
namespace larch {
std::vector<plat_learned_mac_addr> get_learned_mac_addrs()
{
const auto bridge_mapping = sai::get_bridge_port_mapping();
const auto port_name_mapping = sai::get_port_name_mapping();
std::unordered_map<sai::object_id, std::uint16_t> vlan_cache;
std::vector<plat_learned_mac_addr> learned_mac_addrs;
std::int64_t cursor = 0;
std::unordered_set<std::string> keys;
do
{
constexpr std::string_view pattern =
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:*";
keys.clear();
cursor = state->redis_asic->scan(
cursor,
pattern,
std::inserter(keys, keys.begin()));
for (const auto &key : keys)
{
plat_learned_mac_addr learned_entry{};
std::unordered_map<std::string, std::string> entry;
// Get port name
state->redis_asic->hgetall(
key,
std::inserter(entry, entry.begin()));
if (entry.at("SAI_FDB_ENTRY_ATTR_TYPE")
== "SAI_FDB_ENTRY_TYPE_STATIC")
continue;
const auto port_it = bridge_mapping.find(
entry.at("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID")
.substr(sai::oid_prefix.size()));
if (port_it == bridge_mapping.cend())
continue;
const auto interface_it =
port_name_mapping.find(port_it->second);
const std::string interface_name =
(interface_it != port_name_mapping.cend())
? interface_it->second
: port_it->second;
// Get VLAN ID
std::uint16_t vlan_id{};
const json fdb_json =
json::parse(key.substr(pattern.size() - 1));
if (fdb_json.contains("vlan"))
{
vlan_id = static_cast<std::uint16_t>(std::stoul(
fdb_json.at("vlan")
.template get<std::string>()));
}
else
{
if (!fdb_json.contains("bvid"))
continue;
std::string vlan_oid =
fdb_json.at("bvid")
.template get<std::string>()
.substr(sai::oid_prefix.size());
const auto vlan_it = vlan_cache.find(vlan_oid);
if (vlan_it != vlan_cache.cend())
{
// VLAN is found in cache, using it
vlan_id = vlan_it->second;
}
else
{
auto vlan_id_opt =
sai::get_vlan_by_oid(vlan_oid);
if (!vlan_id_opt)
continue;
vlan_id = *vlan_id_opt;
vlan_cache.try_emplace(
std::move(vlan_oid),
std::move(*vlan_id_opt));
}
}
std::snprintf(
learned_entry.port,
PORT_MAX_NAME_LEN,
"%s",
interface_name.c_str());
learned_entry.vid = vlan_id;
std::snprintf(
learned_entry.mac,
PLATFORM_MAC_STR_SIZE,
"%s",
fdb_json.at("mac")
.template get<std::string>()
.c_str());
learned_mac_addrs.push_back(learned_entry);
}
} while (cursor != 0);
return learned_mac_addrs;
}
} // namespace larch

View File

@@ -0,0 +1,14 @@
#ifndef LARCH_PLATFORM_FDB_HPP_
#define LARCH_PLATFORM_FDB_HPP_
#include <ucentral-platform.h>
#include <vector>
namespace larch {
std::vector<plat_learned_mac_addr> get_learned_mac_addrs();
} // namespace larch
#endif // !LARCH_PLATFORM_FDB_HPP_

View File

@@ -0,0 +1,136 @@
#include <igmp.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <string>
using nlohmann::json;
using namespace std;
namespace larch {
typedef enum {
GNMI_IGMP_VERSION_NA = 0,
GNMI_IGMP_VERSION_1 = 1,
GNMI_IGMP_VERSION_2 = 2,
GNMI_IGMP_VERSION_3 = 3
} gnmi_igmp_version_t;
static void disable_igmp_snooping(uint16_t vid)
{
gnmi_operation op;
const auto igmp_snooping_list
= gnmi_get ("/sonic-igmp-snooping:sonic-igmp-snooping/CFG_L2MC_TABLE/"
"CFG_L2MC_TABLE_LIST");
const json igmp_snooping_list_json = json::parse(igmp_snooping_list);
/* There are no IGMP-snoooping */
if (!igmp_snooping_list_json.contains("sonic-igmp-snooping:CFG_L2MC_TABLE_LIST"))
return;
/* Delete igmp-snooping config for specific VLAN. */
op.add_delete("/sonic-igmp-snooping:sonic-igmp-snooping/CFG_L2MC_TABLE/CFG_L2MC_TABLE_LIST[vlan-name=Vlan" + to_string(vid) + "]");
op.execute();
}
static void set_igmp_snooping(uint16_t vid, struct plat_igmp *igmp)
{
bool enabled = igmp->snooping_enabled || igmp->querier_enabled;
gnmi_igmp_version_t gnmi_igmp_version = GNMI_IGMP_VERSION_NA;
gnmi_operation op;
if (!enabled)
{
disable_igmp_snooping(vid);
return;
}
if (igmp->version == PLAT_IGMP_VERSION_1)
gnmi_igmp_version = GNMI_IGMP_VERSION_1;
else if (igmp->version == PLAT_IGMP_VERSION_2)
gnmi_igmp_version = GNMI_IGMP_VERSION_2;
else if (igmp->version == PLAT_IGMP_VERSION_3)
gnmi_igmp_version = GNMI_IGMP_VERSION_3;
else
{
UC_LOG_ERR("Failed IGMP version");
throw std::runtime_error{"Failed IGMP version"};
}
/* Config IGMP-snooping */
json igmp_snooping_list_json;
igmp_snooping_list_json["vlan-name"] = "Vlan" + to_string(vid);
igmp_snooping_list_json["enabled"] = igmp->snooping_enabled;
igmp_snooping_list_json["querier"] = igmp->querier_enabled;
if (igmp->querier_enabled)
{
igmp_snooping_list_json["query-interval"] = igmp->query_interval;
igmp_snooping_list_json["query-max-response-time"] = igmp->max_response_time;
igmp_snooping_list_json["last-member-query-interval"] = igmp->last_member_query_interval;
if (gnmi_igmp_version != GNMI_IGMP_VERSION_NA)
{
igmp_snooping_list_json["version"] = igmp->version;
}
}
if (igmp->snooping_enabled) {
igmp_snooping_list_json["fast-leave"] = igmp->fast_leave_enabled;
}
json add_igmp_snooping_list_json;
add_igmp_snooping_list_json["sonic-igmp-snooping:CFG_L2MC_TABLE_LIST"] = {igmp_snooping_list_json};
op.add_update("/sonic-igmp-snooping:sonic-igmp-snooping/CFG_L2MC_TABLE/CFG_L2MC_TABLE_LIST", add_igmp_snooping_list_json.dump());
op.execute();
}
static void set_igmp_static_groups(uint16_t vid, struct plat_igmp *igmp)
{
struct plat_ports_list *port_node = NULL;
size_t group_idx;
gnmi_operation op;
for (group_idx = 0; group_idx < igmp->num_groups; group_idx++)
{
const std::string ip_addr = addr_to_str(igmp->groups[group_idx].addr);
json igmp_static_group_json;
igmp_static_group_json["vlan-name"] = "Vlan" + to_string(vid);
igmp_static_group_json["group-addr"] = ip_addr;
json out_intf_list_json = json::array();
UCENTRAL_LIST_FOR_EACH_MEMBER(port_node, &igmp->groups[group_idx].egress_ports_list)
{
out_intf_list_json.push_back(port_node->name);
}
igmp_static_group_json["out-intf"] = out_intf_list_json;
json add_igmp_static_group_json;
add_igmp_static_group_json["sonic-igmp-snooping:CFG_L2MC_STATIC_GROUP_TABLE_LIST"] = {igmp_static_group_json};
op.add_update("/sonic-igmp-snooping:sonic-igmp-snooping/CFG_L2MC_STATIC_GROUP_TABLE/CFG_L2MC_STATIC_GROUP_TABLE_LIST", add_igmp_static_group_json.dump());
}
op.execute();
}
void apply_igmp_config(uint16_t vid, struct plat_igmp *igmp)
{
set_igmp_snooping(vid, igmp);
if (igmp->num_groups > 0)
{
set_igmp_static_groups(vid, igmp);
}
}
}

View File

@@ -0,0 +1,12 @@
#ifndef LARCH_PLATFORM_IGMP_HPP_
#define LARCH_PLATFORM_IGMP_HPP_
#include <ucentral-platform.h>
namespace larch {
void apply_igmp_config(uint16_t vid, struct plat_igmp *igmp);
}
#endif // !LARCH_PLATFORM_IGMP_HPP_

View File

@@ -0,0 +1,307 @@
#include <fdb.hpp>
#include <metrics.hpp>
#include <port.hpp>
#include <route.hpp>
#include <nlohmann/json.hpp>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <sys/sysinfo.h>
#include <array>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip> // std::setw
#include <iterator> // std::begin, std::size
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <utility> // std::move
using nlohmann::json;
namespace larch {
namespace {
const std::string metrics_config_path =
"/var/lib/ucentral/metrics_cfg.json";
}
static plat_system_info get_system_info()
{
plat_system_info system_info{};
// Get load average
std::array<double, std::size(system_info.load_average)> load_average{};
if (getloadavg(load_average.data(), load_average.size()) < 0)
{
UC_LOG_ERR("Failed to get load average");
}
else
{
for (double &elem : load_average)
elem /= 100.0;
std::copy(
load_average.cbegin(),
load_average.cend(),
std::begin(system_info.load_average));
}
// Get RAM cached
std::ifstream is{"/proc/meminfo"};
if (!is)
{
UC_LOG_ERR("Failed to get memory info");
}
else
{
std::string line;
std::uint64_t cached = 0;
bool found = false;
while (std::getline(is, line))
{
if (std::sscanf(line.c_str(), "Cached:%lu", &cached)
== 1)
{
system_info.ram_cached = cached * 1024;
found = true;
}
}
if (!found)
{
UC_LOG_ERR("Can't find Cached entry in /proc/meminfo");
}
}
// Get other system information
struct sysinfo sys_info = {};
if (sysinfo(&sys_info) < 0)
{
UC_LOG_ERR(
"Failed to get system info: %s",
std::strerror(errno));
}
else
{
system_info.localtime =
static_cast<std::uint64_t>(std::time(nullptr));
system_info.uptime = sys_info.uptime;
system_info.ram_buffered =
sys_info.bufferram * sys_info.mem_unit;
system_info.ram_free =
(sys_info.freeram + sys_info.freeswap) * sys_info.mem_unit;
system_info.ram_total = sys_info.totalram * sys_info.mem_unit;
}
return system_info;
}
std::pair<plat_state_info, state_data> get_state_info()
{
plat_state_info state_info{};
state_data data{};
state_info.system_info = get_system_info();
// Get port info
try
{
data.port_info = get_port_info();
state_info.port_info = data.port_info.data();
state_info.port_info_count = data.port_info.size();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to get port info: %s", ex.what());
}
// Get learned MAC addresses
try
{
data.learned_mac_addrs = get_learned_mac_addrs();
state_info.learned_mac_list = data.learned_mac_addrs.data();
state_info.learned_mac_list_size =
data.learned_mac_addrs.size();
}
catch (const std::exception &ex)
{
UC_LOG_ERR(
"Failed to get learned MAC addresses: %s",
ex.what());
}
// Get GW addresses
try
{
data.gw_addresses = get_gw_addresses();
state_info.gw_addr_list = data.gw_addresses.data();
state_info.gw_addr_list_size = data.gw_addresses.size();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to get GW addresses: %s", ex.what());
}
return {std::move(state_info), std::move(data)};
}
void save_metrics_config(const plat_metrics_cfg *cfg)
{
json metrics_cfg;
json &telemetry_cfg = metrics_cfg["telemetry"];
telemetry_cfg["enabled"] = static_cast<bool>(cfg->telemetry.enabled);
telemetry_cfg["interval"] = cfg->telemetry.interval;
json &healthcheck_cfg = metrics_cfg["healthcheck"];
healthcheck_cfg["enabled"] =
static_cast<bool>(cfg->healthcheck.enabled);
healthcheck_cfg["interval"] = cfg->healthcheck.interval;
json &state_cfg = metrics_cfg["state"];
state_cfg["enabled"] = static_cast<bool>(cfg->state.enabled);
state_cfg["lldp_enabled"] = static_cast<bool>(cfg->state.lldp_enabled);
state_cfg["clients_enabled"] =
static_cast<bool>(cfg->state.clients_enabled);
state_cfg["interval"] = cfg->state.interval;
state_cfg["max_mac_count"] = cfg->state.max_mac_count;
state_cfg["public_ip_lookup"] = cfg->state.public_ip_lookup;
std::ofstream os{metrics_config_path};
os << std::setw(4) << metrics_cfg << std::endl;
if (!os)
{
throw std::runtime_error{
"Failed to write metrics config to the file"};
}
}
void load_metrics_config(plat_metrics_cfg *cfg)
{
std::ifstream is{metrics_config_path};
// Metrics configuration doesn't exist yet, return silently without any
// error
if (!is.is_open())
return;
json metrics_cfg = json::parse(is);
if (metrics_cfg.contains("telemetry"))
{
const json &telemetry_cfg = metrics_cfg.at("telemetry");
cfg->telemetry.enabled =
telemetry_cfg.at("enabled").template get<bool>();
cfg->telemetry.interval =
telemetry_cfg.at("interval").template get<std::size_t>();
}
if (metrics_cfg.contains("healthcheck"))
{
const json &healthcheck_cfg = metrics_cfg.at("healthcheck");
cfg->healthcheck.enabled =
healthcheck_cfg.at("enabled").template get<bool>();
cfg->healthcheck.interval =
healthcheck_cfg.at("interval").template get<std::size_t>();
}
if (metrics_cfg.contains("state"))
{
const json &state_cfg = metrics_cfg.at("state");
cfg->state.enabled =
state_cfg.at("enabled").template get<bool>();
cfg->state.lldp_enabled =
state_cfg.at("lldp_enabled").template get<bool>();
cfg->state.clients_enabled =
state_cfg.at("clients_enabled").template get<bool>();
cfg->state.interval =
state_cfg.at("interval").template get<std::size_t>();
cfg->state.max_mac_count =
state_cfg.at("max_mac_count").template get<std::size_t>();
std::strncpy(
cfg->state.public_ip_lookup,
state_cfg.at("public_ip_lookup")
.template get<std::string>()
.c_str(),
std::size(cfg->state.public_ip_lookup) - 1);
}
}
periodic::~periodic()
{
if (thread_ && thread_->joinable())
thread_->join();
}
void periodic::start(
std::function<void()> callback,
std::chrono::seconds period)
{
if (thread_)
stop();
callback_ = std::move(callback);
period_ = std::move(period);
thread_ =
std::make_unique<std::thread>(std::bind(&periodic::worker, this));
}
void periodic::stop()
{
if (!thread_)
return;
{
std::scoped_lock lk{mut_};
stop_signal_ = true;
}
cv_.notify_one();
if (thread_->joinable())
thread_->join();
thread_.reset();
stop_signal_ = false;
}
void periodic::worker()
{
std::unique_lock lk{mut_};
while (!stop_signal_)
{
try
{
callback_();
}
catch (const std::exception &ex)
{
UC_LOG_ERR(
"Error occurred during periodic task execution: %s",
ex.what());
}
cv_.wait_for(lk, period_, [this] { return stop_signal_; });
}
}
} // namespace larch

View File

@@ -0,0 +1,62 @@
#ifndef LARCH_PLATFORM_METRICS_HPP_
#define LARCH_PLATFORM_METRICS_HPP_
#include <state.hpp>
#include <ucentral-platform.h>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility> // std::pair
#include <vector>
namespace larch {
struct state_data {
std::vector<plat_port_info> port_info;
std::vector<plat_learned_mac_addr> learned_mac_addrs;
std::vector<plat_gw_address> gw_addresses;
};
/**
* @brief Get state information.
*
* @return A pair of @c plat_state_info and @c state_data. The latter is used as
* an actual storage for all the gathered information, while the former
* references the data in it. Pay attention to the fact that @c state_data must
* outlive @c plat_state_info.
*/
std::pair<plat_state_info, state_data> get_state_info();
void save_metrics_config(const plat_metrics_cfg *cfg);
void load_metrics_config(plat_metrics_cfg *cfg);
class periodic {
public:
periodic() = default;
~periodic();
void start(std::function<void()> callback, std::chrono::seconds period);
void stop();
protected:
void worker();
std::unique_ptr<std::thread> thread_;
std::condition_variable cv_;
std::mutex mut_;
bool stop_signal_ = false;
std::chrono::seconds period_{};
std::function<void()> callback_;
};
} // namespace larch
#endif // !LARCH_PLATFORM_METRICS_HPP_

View File

@@ -0,0 +1,631 @@
#include <metrics.hpp>
#include <port.hpp>
#include <route.hpp>
#include <services.hpp>
#include <state.hpp>
#include <stp.hpp>
#include <syslog.hpp>
#include <utils.hpp>
#include <vlan.hpp>
#include <gnmi.grpc.pb.h>
#include <gnmi.pb.h>
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
#include <grpc/grpc.h>
#include <nlohmann/json.hpp>
#include <sw/redis++/redis++.h>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <router-utils.h>
#include <cerrno>
#include <chrono>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <memory>
#include <stdexcept>
#include <string>
#include <thread>
#include <utility> // std::move
#include <sys/types.h>
#include <sys/wait.h>
#define UNUSED_PARAM(param) (void)((param))
#define RTTY_SESS_MAX (10)
using nlohmann::json;
namespace {
const std::string config_id_path = "/var/lib/ucentral/saved_config_id";
}
int plat_init(void)
{
using namespace larch;
state = std::make_unique<platform_state>();
auto verifier = grpc::experimental::ExternalCertificateVerifier::Create<certificate_verifier>();
grpc::experimental::TlsChannelCredentialsOptions options;
options.set_verify_server_certs(false);
options.set_certificate_verifier(verifier);
options.set_check_call_host(false);
auto credentials = grpc::experimental::TlsCredentials(options);
state->channel = grpc::CreateChannel("127.0.0.1:8080", credentials);
state->gnmi_stub = gnmi::gNMI::NewStub(state->channel);
// created new channel for gnoi
state->system_gnoi_stub = gnoi::system::System::NewStub(grpc::CreateChannel("127.0.0.1:8080", credentials));
state->stub_gnoi_sonic = gnoi::sonic::SonicService::NewStub(grpc::CreateChannel("127.0.0.1:8080", credentials));
state->telemetry_periodic = std::make_unique<periodic>();
state->state_periodic = std::make_unique<periodic>();
state->health_periodic = std::make_unique<periodic>();
state->redis_asic = std::make_unique<sw::redis::Redis>("tcp://127.0.0.1:6379/1");
state->redis_counters = std::make_unique<sw::redis::Redis>("tcp://127.0.0.1:6379/2");
/*
* Workaround to fix the issue when ucentral-client starts too early
* (before gNMI container or before ports are up)
*/
UC_LOG_INFO("Trying to get initial port list...");
std::vector<port> ports;
while (true)
{
try
{
ports = get_port_list();
if (ports.empty())
{
UC_LOG_DBG("Port list is empty");
}
else
{
break;
}
}
catch (const std::exception &ex)
{
UC_LOG_DBG(
"Failed to get initial port list: %s",
ex.what());
}
UC_LOG_DBG("Retrying in 10 seconds...");
std::this_thread::sleep_for(std::chrono::seconds{10});
}
UC_LOG_INFO("Successfully got the port list, continuing platform "
"initialization");
try
{
/*
* Get the state of interfaces addresses
*/
const plat_ipv4 no_address{false};
for (port &p : get_port_list())
{
const auto addresses = get_port_addresses(p);
state->interfaces_addrs.push_back(
addresses.empty() ? no_address : addresses[0]);
}
/*
* Initialize the router
*/
ucentral_router_fib_db_free(&state->router);
auto routes = get_routes(0);
if (ucentral_router_fib_db_alloc(&state->router, routes.size())
!= 0)
{
UC_LOG_CRIT("Failed to allocate FIB DB");
return -1;
}
for (auto &node : routes)
{
if (ucentral_router_fib_db_append(&state->router, &node)
!= 0)
{
UC_LOG_CRIT("Failed to append entry to FIB DB");
return -1;
}
}
}
catch (const std::exception &ex)
{
UC_LOG_CRIT("Platform initialization failed: %s", ex.what());
return -1;
}
return 0;
}
int plat_info_get(struct plat_platform_info *info)
{
using namespace larch;
try
{
const json metadata_json =
json::parse(
gnmi_get("/sonic-device_metadata:sonic-device_metadata/"
"DEVICE_METADATA/localhost"))
.at("sonic-device_metadata:localhost");
auto copy_from_json =
[](const json &obj, char *dest, std::size_t dest_size) {
std::strncpy(
dest,
obj.template get<std::string>().c_str(),
dest_size > 0 ? dest_size - 1 : 0);
};
copy_from_json(
metadata_json.at("platform"),
info->platform,
std::size(info->platform));
copy_from_json(
metadata_json.at("hwsku"),
info->hwsku,
std::size(info->hwsku));
copy_from_json(
metadata_json.at("mac"),
info->mac,
std::size(info->mac));
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to get device metadata: %s", ex.what());
return -1;
}
return 0;
}
int plat_reboot(void)
{
grpc::Status status;
gnoi::system::RebootResponse gres;
gnoi::system::RebootRequest greq;
grpc::ClientContext context;
status = (larch::state->system_gnoi_stub)->Reboot(&context, greq, &gres);
if (!status.ok()) {
UC_LOG_ERR("Request failed");
UC_LOG_ERR("Code: %d", status.error_code());
return 1;
}
return 0;
}
int plat_config_apply(struct plat_cfg *cfg, uint32_t id)
{
using namespace larch;
UNUSED_PARAM(id);
try
{
apply_vlan_config(cfg);
apply_port_config(cfg);
apply_route_config(cfg);
apply_stp_config(cfg);
apply_services_config(cfg);
apply_vlan_ipv4_config(cfg);
/* apply_syslog_config() call must always be the last one */
apply_syslog_config(cfg->log_cfg, cfg->log_cfg_cnt);
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to apply config: %s", ex.what());
return -1;
}
return 0;
}
int plat_config_save(uint64_t id)
{
// TO-DO: actually save the config, not only config id
std::ofstream os{config_id_path};
if (!os)
{
UC_LOG_ERR(
"Failed to save config id - can't open the file: %s",
std::strerror(errno));
return -1;
}
os << id << std::endl;
return 0;
}
int plat_config_restore(void)
{
return 0;
}
int plat_metrics_save(const struct plat_metrics_cfg *cfg)
{
try
{
larch::save_metrics_config(cfg);
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to save metrics config: %s", ex.what());
return -1;
}
return 0;
}
int plat_metrics_restore(struct plat_metrics_cfg *cfg)
{
try
{
larch::load_metrics_config(cfg);
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to load metrics config: %s", ex.what());
return -1;
}
return 0;
}
int plat_saved_config_id_get(uint64_t *id)
{
std::ifstream is{config_id_path};
if (!is)
{
UC_LOG_ERR(
"Failed to get saved config id - can't open the file: %s",
std::strerror(errno));
return -1;
}
is >> *id;
if (!is.good())
{
UC_LOG_ERR("Failed to get saved config id - read failed");
return -1;
}
return 0;
}
void plat_config_destroy(struct plat_cfg *cfg)
{
UNUSED_PARAM(cfg);
}
int plat_factory_default(void)
{
return 0;
}
int plat_rtty(struct plat_rtty_cfg *rtty_cfg)
{
static pid_t child[RTTY_SESS_MAX];
int n, i, e;
/* wait the dead children */
for (i = 0; i < RTTY_SESS_MAX;) {
n = 0;
if (child[i] > 0) {
while ((n = waitpid(child[i], 0, WNOHANG)) < 0 && errno == EINTR);
}
if (n <= 0) {
++i;
} else {
if (RTTY_SESS_MAX > 1)
memmove(&child[i], &child[i+1], (RTTY_SESS_MAX-i-1)*sizeof(pid_t));
child[RTTY_SESS_MAX - 1] = -1;
}
}
/* find a place for a new session */
for (i = 0; i < RTTY_SESS_MAX && child[i] > 0; ++i);
/* if there are RTTY_SESS_MAX sessions, kill the oldest */
if (i == RTTY_SESS_MAX) {
if (child[0] <= 0) {
UC_LOG_CRIT("child[0]==%jd", (intmax_t)child[0]);
} else {
if (kill(child[0], SIGKILL)) {
UC_LOG_CRIT("kill failed: %s", strerror(errno));
} else {
while ((n = waitpid(child[0], 0, 0)) < 0 && errno == EINTR);
if (n < 0)
UC_LOG_CRIT("waitpid failed: %s", strerror(errno));
}
if (RTTY_SESS_MAX > 1)
memmove(&child[0], &child[1], (RTTY_SESS_MAX - 1) * sizeof(pid_t));
}
i = RTTY_SESS_MAX - 1;
}
child[i] = fork();
if (!child[i]) {
char argv[][128] = {
"--id=",
"--host=",
"--port=",
"--token="
};
setsid();
strcat(argv[0], rtty_cfg->id);
strcat(argv[1], rtty_cfg->server);
sprintf(argv[2], "--port=%u", rtty_cfg->port);
strcat(argv[3], rtty_cfg->token);
execl("/usr/local/bin/rtty", "rtty", argv[0], argv[1], argv[2], argv[3], "-d Larch Switch device", "-v", "-s", NULL);
e = errno;
UC_LOG_DBG("execv failed %d\n", e);
/* If we got to this line, that means execl failed, and
* currently, due to simple design (fork/exec), it's impossible
* to notify <main> process, that forked child failed to execl.
* TBD: notify about execl fail.
*/
_exit(e);
}
if (child[i] < (pid_t)0) {
return -1;
}
return 0;
}
int plat_upgrade(char *uri, char *signature)
{
UNUSED_PARAM(signature);
UNUSED_PARAM(uri);
return 0;
}
char *plat_log_pop(void)
{
return NULL;
}
void plat_log_flush(void)
{
}
char *plat_log_pop_concatenate(void)
{
return NULL;
}
int plat_event_subscribe(const struct plat_event_callbacks *cbs)
{
UNUSED_PARAM(cbs);
return 0;
}
void plat_event_unsubscribe(void)
{
}
void plat_health_poll(void (*cb)(struct plat_health_info *), int period_sec)
{
using namespace larch;
try
{
state->health_periodic->stop();
state->health_periodic->start(
[cb] {
plat_health_info health_info{100};
cb(&health_info);
},
std::chrono::seconds{period_sec});
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to start health poll: %s", ex.what());
}
}
void plat_health_poll_stop(void)
{
using namespace larch;
try
{
state->health_periodic->stop();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to stop health poll: %s", ex.what());
}
}
void plat_telemetry_poll(void (*cb)(struct plat_state_info *), int period_sec)
{
using namespace larch;
try
{
state->telemetry_periodic->stop();
state->telemetry_periodic->start(
[cb] {
auto [state_info, data] = get_state_info();
cb(&state_info);
},
std::chrono::seconds{period_sec});
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to start telemetry poll: %s", ex.what());
}
}
void plat_telemetry_poll_stop(void)
{
using namespace larch;
try
{
state->telemetry_periodic->stop();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to stop telemetry poll: %s", ex.what());
}
}
void plat_state_poll(void (*cb)(struct plat_state_info *), int period_sec)
{
using namespace larch;
try
{
state->state_periodic->stop();
state->state_periodic->start(
[cb] {
auto [state_info, data] = get_state_info();
cb(&state_info);
},
std::chrono::seconds{period_sec});
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to start state poll: %s", ex.what());
}
}
void plat_state_poll_stop(void)
{
using namespace larch;
try
{
state->state_periodic->stop();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to stop state poll: %s", ex.what());
}
}
void plat_upgrade_poll(int (*cb)(struct plat_upgrade_info *), int period_sec)
{
UNUSED_PARAM(period_sec);
UNUSED_PARAM(cb);
}
void plat_upgrade_poll_stop(void)
{
}
int plat_run_script(struct plat_run_script *)
{
return 0;
}
int plat_port_list_get(uint16_t list_size, struct plat_ports_list *ports)
{
try
{
const auto port_list = larch::get_port_list();
if (port_list.size() < list_size)
{
UC_LOG_ERR(
"Too much ports requested (requested %hu, while "
"only %zu available)",
list_size,
port_list.size());
return -1;
}
auto it = port_list.cbegin();
for (plat_ports_list *node = ports; node; node = node->next)
{
std::strncpy(
node->name,
it++->name.c_str(),
sizeof(node->name) - 1);
}
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to get list of ports: %s", ex.what());
return -1;
}
return 0;
}
int plat_port_num_get(uint16_t *num_of_active_ports)
{
try
{
*num_of_active_ports = larch::get_port_list().size();
}
catch (const std::exception &ex)
{
UC_LOG_ERR("Failed to get count of ports: %s", ex.what());
return -1;
}
return 0;
}
int plat_running_img_name_get(char *str, size_t str_max_len)
{
UNUSED_PARAM(str_max_len);
UNUSED_PARAM(str);
return 0;
}
int plat_revision_get(char *str, size_t str_max_len)
{
UNUSED_PARAM(str_max_len);
UNUSED_PARAM(str);
return 0;
}
int plat_reboot_cause_get(struct plat_reboot_cause *cause)
{
UNUSED_PARAM(cause);
return 0;
}
int plat_diagnostic(char *res_path)
{
UNUSED_PARAM(res_path);
return 0;
}

View File

@@ -0,0 +1,528 @@
#include <port.hpp>
#include <state.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#include <bitmap.h>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <arpa/inet.h>
#include <algorithm> // std::find_if
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio> // std::snprintf, std::sscanf
#include <cstring>
#include <optional>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility> // std::move
#include <vector>
using nlohmann::json;
namespace larch {
std::vector<port> get_port_list()
{
const json port_list_json =
json::parse(gnmi_get("/sonic-port:sonic-port/PORT/PORT_LIST"));
std::vector<port> port_list;
for (const auto port_json :
port_list_json.value("sonic-port:PORT_LIST", json::array()))
{
port &p = port_list.emplace_back();
p.name = port_json.at("name").template get<std::string>();
}
return port_list;
}
static bool get_port_oper_status(const std::string &port_name)
{
std::string port_status_data;
try
{
port_status_data = gnmi_get(
"/openconfig-interfaces:interfaces/interface[name="
+ port_name + "]/state/oper-status");
}
catch (const gnmi_exception &ex)
{
// For some reason there's no oper-status field in the gNMI
// response when carrier is down
return false;
}
const json port_status_json = json::parse(port_status_data);
const std::string port_status_str =
port_status_json.at("openconfig-interfaces:oper-status")
.template get<std::string>();
if (port_status_str == "UP")
return true;
else if (port_status_str == "DOWN")
return false;
else
{
UC_LOG_ERR(
"Unknown port oper status: %s",
port_status_str.c_str());
throw std::runtime_error{
"Unknown oper status: " + port_status_str};
}
}
static void set_port_admin_state(const std::string &port_name, bool state)
{
json port_state_json;
port_state_json["openconfig-interfaces:config"]["enabled"] = state;
gnmi_set(
"/openconfig-interfaces:interfaces/interface[name=" + port_name
+ "]/config",
port_state_json.dump());
}
static std::uint32_t get_port_speed(const std::string &port_name)
{
const json port_speed_json = json::parse(gnmi_get(
"/sonic-port:sonic-port/PORT/PORT_LIST[name=" + port_name
+ "]/speed"));
return port_speed_json["sonic-port:speed"]
.template get<std::uint32_t>();
}
static void set_port_speed(const std::string &port_name, std::uint32_t speed)
{
auto speed_to_num = [](std::uint32_t speed) -> std::uint32_t {
switch (speed)
{
case UCENTRAL_PORT_SPEED_10_E:
return 10;
case UCENTRAL_PORT_SPEED_100_E:
return 100;
case UCENTRAL_PORT_SPEED_1000_E:
return 1000;
case UCENTRAL_PORT_SPEED_2500_E:
return 2500;
case UCENTRAL_PORT_SPEED_5000_E:
return 5000;
case UCENTRAL_PORT_SPEED_10000_E:
return 10000;
case UCENTRAL_PORT_SPEED_25000_E:
return 25000;
case UCENTRAL_PORT_SPEED_40000_E:
return 40000;
case UCENTRAL_PORT_SPEED_100000_E:
return 100000;
default:
{
UC_LOG_ERR("Unknown port speed");
throw std::runtime_error{"Unknown port speed"};
}
}
};
json port_speed_json;
port_speed_json["name"] = port_name;
port_speed_json["speed"] = speed_to_num(speed);
json set_port_speed_json;
set_port_speed_json["sonic-port:PORT_LIST"] = {port_speed_json};
gnmi_set(
"/sonic-port:sonic-port/PORT/PORT_LIST[name=" + port_name + "]",
set_port_speed_json.dump());
}
static void set_port_mtu(const std::string &port_name, std::uint16_t mtu)
{
json port_mtu_json;
port_mtu_json["name"] = port_name;
port_mtu_json["mtu"] = mtu;
json set_port_mtu_json;
set_port_mtu_json["sonic-port:PORT_LIST"] = {port_mtu_json};
gnmi_set(
"/sonic-port:sonic-port/PORT/PORT_LIST[name=" + port_name + "]",
set_port_mtu_json.dump());
}
static std::unordered_map<std::string, std::uint64_t>
get_port_counters(const std::string &port_name)
{
const json port_counters_json = json::parse(gnmi_get(
"/openconfig-interfaces:interfaces/interface[name=" + port_name
+ "]/state/counters"));
std::unordered_map<std::string, std::uint64_t> counters;
if (!port_counters_json.contains("openconfig-interfaces:counters"))
return counters;
for (const auto &item :
port_counters_json["openconfig-interfaces:counters"].items())
{
counters[item.key()] =
std::stoull(item.value().template get<std::string>());
}
return counters;
}
static std::optional<plat_port_lldp_peer_info>
get_lldp_peer_info(const std::string &port_name)
{
/*
* Actually, more specific YANG path should be used here
* (/openconfig-lldp:lldp/interfaces/interface[name=<interface>]/neighbors/neighbor[id=<interface>])
* but for some reason gNMI response for this path is empty, so the
* workaround is to make more generic request and filter the response
* and find necessary data.
*/
const json lldp_json = json::parse(gnmi_get(
"/openconfig-lldp:lldp/interfaces/interface[name=" + port_name
+ "]"));
const auto &neighbors = lldp_json.at("openconfig-lldp:interface")
.at(0)
.at("neighbors")
.at("neighbor");
const auto neighbor_it = std::find_if(
neighbors.cbegin(),
neighbors.cend(),
[&port_name](const auto &neighbor) {
return neighbor.at("id").template get<std::string>()
== port_name;
});
if (neighbor_it == neighbors.cend())
{
throw std::runtime_error{"Failed to find LLDP neighbor"};
}
if (!neighbor_it->contains("capabilities"))
{
return std::nullopt;
}
plat_port_lldp_peer_info peer_info{};
for (const auto &cap : neighbor_it->at("capabilities").at("capability"))
{
const std::string name =
cap.at("name").template get<std::string>();
const bool enabled =
cap.at("state").at("enabled").template get<bool>();
if (name == "openconfig-lldp-types:MAC_BRIDGE")
peer_info.capabilities.is_bridge = enabled;
else if (name == "openconfig-lldp-types:ROUTER")
peer_info.capabilities.is_router = enabled;
else if (name == "openconfig-lldp-types:WLAN_ACCESS_POINT")
peer_info.capabilities.is_wlan_ap = enabled;
else if (name == "openconfig-lldp-types:STATION_ONLY")
peer_info.capabilities.is_station = enabled;
}
const json &state = neighbor_it->at("state");
std::strncpy(
peer_info.name,
state.at("system-name").template get<std::string>().c_str(),
std::size(peer_info.name) - 1);
std::strncpy(
peer_info.description,
state.at("system-description").template get<std::string>().c_str(),
std::size(peer_info.description) - 1);
std::strncpy(
peer_info.mac,
state.at("chassis-id").template get<std::string>().c_str(),
std::size(peer_info.mac) - 1);
std::strncpy(
peer_info.port,
neighbor_it->at("id").template get<std::string>().c_str(),
std::size(peer_info.port) - 1);
// Parse management addresses
const auto addresses = split_string(
state.at("management-address").template get<std::string>(),
",");
for (std::size_t i = 0; i < UCENTRAL_PORT_LLDP_PEER_INFO_MAX_MGMT_IPS;
++i)
{
if (i >= addresses.size())
break;
const char *address = addresses[i].c_str();
// Verify that retrieved address is either valid IPv4 or IPv6
// address. If so - copy it to peer_info.
bool success = false;
std::array<unsigned char, sizeof(in6_addr)> addr_buf{};
if (inet_pton(AF_INET, address, addr_buf.data()) == 1)
success = true;
else if (inet_pton(AF_INET6, address, addr_buf.data()) == 1)
success = true;
if (success)
std::strncpy(
peer_info.mgmt_ips[i],
address,
INET6_ADDRSTRLEN);
}
return peer_info;
}
std::vector<plat_ipv4> get_port_addresses(const port &p)
{
// TO-DO: should gnmi_exception be caught (this would mean that there're
// no addresses assigned to interface)?
const json addresses_json = json::parse(gnmi_get(
"/openconfig-interfaces:interfaces/interface[name=" + p.name
+ "]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/"
"addresses"));
if (!addresses_json.contains("openconfig-if-ip:addresses"))
return {};
std::vector<plat_ipv4> addresses;
for (const auto &address_json :
addresses_json.at("openconfig-if-ip:addresses")
.value("address", json::array()))
{
const json &config_json = address_json.at("config");
const std::string address_str =
config_json.at("ip").template get<std::string>();
plat_ipv4 address{};
if (inet_pton(AF_INET, address_str.c_str(), &address.subnet)
!= 1)
{
UC_LOG_ERR(
"Failed to parse interface IP address: %s",
address_str.c_str());
continue;
}
address.subnet_len = config_json.at("prefix-length")
.template get<std::int32_t>();
if (address.subnet_len < 0 || address.subnet_len > 32)
{
UC_LOG_ERR(
"Incorrect subnet length: %d (address %s)",
address.subnet_len,
address_str.c_str());
continue;
}
address.exist = true;
addresses.push_back(std::move(address));
}
return addresses;
}
static void
add_port_address(const std::string &port_name, const plat_ipv4 &address)
{
const std::string addr_str = addr_to_str(address.subnet);
json address_json;
address_json["ip"] = addr_str;
address_json["config"]["ip"] = addr_str;
address_json["config"]["prefix-length"] = address.subnet_len;
json port_json;
port_json["index"] = 0;
port_json["openconfig-if-ip:ipv4"]["addresses"]["address"] = {
address_json};
json add_port_json;
add_port_json["openconfig-interfaces:subinterface"] = {port_json};
gnmi_set(
"/openconfig-interfaces:interfaces/interface[name=" + port_name
+ "]/subinterfaces/subinterface",
add_port_json.dump());
}
static void
delete_port_address(const std::string &port_name, const plat_ipv4 &address)
{
const std::string addr_str = addr_to_str(address.subnet);
gnmi_operation op;
op.add_delete(
"/openconfig-interfaces:interfaces/interface[name=" + port_name
+ "]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/"
"addresses/address[ip="
+ addr_str + "]");
op.execute();
}
void apply_port_config(plat_cfg *cfg)
{
std::size_t i = 0;
BITMAP_FOR_EACH_BIT_SET(i, cfg->ports_to_cfg, MAX_NUM_OF_PORTS)
{
const plat_port &port = cfg->ports[i];
const std::string port_name = "Ethernet" + std::to_string(i);
const bool admin_state = port.state == UCENTRAL_PORT_ENABLED_E;
set_port_admin_state(port_name, admin_state);
if (admin_state)
{
set_port_speed(port_name, port.speed);
set_port_mtu(
port_name,
cfg->jumbo_frames ? 9216 : 1500);
}
/*
* Configure the interface address
*/
const plat_ipv4 &address = cfg->portsl2[i].ipv4;
plat_ipv4 &port_addr = state->interfaces_addrs.at(i);
if (address.exist)
{
if (!port_addr.exist
|| port_addr.subnet.s_addr != address.subnet.s_addr
|| port_addr.subnet_len != address.subnet_len)
{
if (port_addr.exist)
{
delete_port_address(
port_name,
port_addr);
}
add_port_address(port_name, address);
port_addr = address;
}
}
else if (port_addr.exist)
{
delete_port_address(port_name, port_addr);
port_addr = plat_ipv4{false};
}
}
}
std::vector<plat_port_info> get_port_info()
{
std::vector<port> ports = get_port_list();
std::vector<plat_port_info> ports_info(ports.size());
std::size_t i = 0;
for (auto &port_info : ports_info)
{
const std::string &port_name = ports[i++].name;
std::snprintf(
port_info.name,
PORT_MAX_NAME_LEN,
"%s",
port_name.c_str());
port_info.speed = get_port_speed(port_name);
port_info.duplex = true;
port_info.carrier_up = get_port_oper_status(port_name);
// Get port counters
std::unordered_map<std::string, std::uint64_t> counters;
try
{
counters = get_port_counters(port_name);
}
catch (const gnmi_exception &ex)
{
UC_LOG_ERR(
"Couldn't get counters for port %s: %s",
port_name.c_str(),
ex.what());
}
auto get_counter =
[&counters](const std::string &counter) -> std::uint64_t {
const auto it = counters.find(counter);
return it != counters.cend() ? it->second : 0;
};
auto &stats = port_info.stats;
stats.collisions = 0;
stats.multicast = 0;
stats.rx_bytes = get_counter("in-octets");
stats.rx_dropped = get_counter("in-discards");
stats.rx_error = get_counter("in-errors");
stats.rx_packets = get_counter("in-unicast-pkts")
+ get_counter("in-multicast-pkts")
+ get_counter("in-broadcast-pkts");
stats.tx_bytes = get_counter("out-octets");
stats.tx_dropped = get_counter("out-discards");
stats.tx_error = get_counter("out-errors");
stats.tx_packets = get_counter("out-unicast-pkts")
+ get_counter("out-multicast-pkts")
+ get_counter("out-broadcast-pkts");
try
{
auto peer_info_opt = get_lldp_peer_info(port_name);
if (peer_info_opt)
{
port_info.lldp_peer_info =
std::move(*peer_info_opt);
port_info.has_lldp_peer_info = 1;
}
}
catch (const std::exception &ex)
{
UC_LOG_DBG(
"Couldn't get LLDP peer info: %s",
ex.what());
}
}
return ports_info;
}
} // namespace larch

View File

@@ -0,0 +1,26 @@
#ifndef LARCH_PLATFORM_PORT_HPP_
#define LARCH_PLATFORM_PORT_HPP_
#include <ucentral-platform.h>
#include <cstddef>
#include <string>
#include <vector>
namespace larch {
struct port {
std::string name;
};
std::vector<port> get_port_list();
std::vector<plat_ipv4> get_port_addresses(const port &p);
void apply_port_config(plat_cfg *cfg);
std::vector<plat_port_info> get_port_info();
} // namespace larch
#endif // !LARCH_PLATFORM_PORT_HPP_

View File

@@ -0,0 +1,27 @@
file(GLOB proto_files CONFIGURE_DEPENDS "*.proto")
add_library(proto-objects OBJECT ${proto_files})
target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf gRPC::grpc++)
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(PROTO_IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}")
target_include_directories(proto-objects PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>")
file(MAKE_DIRECTORY ${PROTO_BINARY_DIR})
protobuf_generate(
TARGET proto-objects
IMPORT_DIRS ${PROTO_IMPORT_DIRS}
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}"
)
protobuf_generate(
TARGET proto-objects
LANGUAGE grpc
GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc
PLUGIN "protoc-gen-grpc=\$<TARGET_FILE:gRPC::grpc_cpp_plugin>"
IMPORT_DIRS ${PROTO_IMPORT_DIRS}
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}"
)

View File

@@ -0,0 +1,49 @@
//
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
syntax = "proto3";
package gnoi.common;
import "types.proto";
option go_package = "github.com/openconfig/gnoi/common";
// RemoteDownload defines the details for a device to initiate a file transfer
// from or to a remote location.
message RemoteDownload {
// The path information containing where to download the data from or to.
// For HTTP(S), this will be the URL (i.e. foo.com/file.tbz2).
// For SFTP and SCP, this will be the address:/path/to/file
// (i.e. host.foo.com:/bar/baz).
string path = 1;
enum Protocol {
UNKNOWN = 0;
SFTP = 1;
HTTP = 2;
HTTPS = 3;
SCP = 4;
}
Protocol protocol = 2;
types.Credentials credentials = 3;
// Optional source address used to initiate connections from the device.
// It can be either an IPv4 address or an IPv6 address, depending on the
// connection's destination address.
string source_address = 4;
}

View File

@@ -0,0 +1,457 @@
//
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
syntax = "proto3";
import "google/protobuf/any.proto";
import "google/protobuf/descriptor.proto";
import "gnmi_ext.proto";
// Package gNMI defines a service specification for the gRPC Network Management
// Interface. This interface is defined to be a standard interface via which
// a network management system ("client") can subscribe to state values,
// retrieve snapshots of state information, and manipulate the state of a data
// tree supported by a device ("target").
//
// This document references the gNMI Specification which can be found at
// http://github.com/openconfig/reference/blob/master/rpc/gnmi
package gnmi;
// Define a protobuf FileOption that defines the gNMI service version.
extend google.protobuf.FileOptions {
// The gNMI service semantic version.
string gnmi_service = 1001;
}
// gNMI_service is the current version of the gNMI service, returned through
// the Capabilities RPC.
option (gnmi_service) = "0.7.0";
service gNMI {
// Capabilities allows the client to retrieve the set of capabilities that
// is supported by the target. This allows the target to validate the
// service version that is implemented and retrieve the set of models that
// the target supports. The models can then be specified in subsequent RPCs
// to restrict the set of data that is utilized.
// Reference: gNMI Specification Section 3.2
rpc Capabilities(CapabilityRequest) returns (CapabilityResponse);
// Retrieve a snapshot of data from the target. A Get RPC requests that the
// target snapshots a subset of the data tree as specified by the paths
// included in the message and serializes this to be returned to the
// client using the specified encoding.
// Reference: gNMI Specification Section 3.3
rpc Get(GetRequest) returns (GetResponse);
// Set allows the client to modify the state of data on the target. The
// paths to modified along with the new values that the client wishes
// to set the value to.
// Reference: gNMI Specification Section 3.4
rpc Set(SetRequest) returns (SetResponse);
// Subscribe allows a client to request the target to send it values
// of particular paths within the data tree. These values may be streamed
// at a particular cadence (STREAM), sent one off on a long-lived channel
// (POLL), or sent as a one-off retrieval (ONCE).
// Reference: gNMI Specification Section 3.5
rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse);
}
// Notification is a re-usable message that is used to encode data from the
// target to the client. A Notification carries two types of changes to the data
// tree:
// - Deleted values (delete) - a set of paths that have been removed from the
// data tree.
// - Updated values (update) - a set of path-value pairs indicating the path
// whose value has changed in the data tree.
// Reference: gNMI Specification Section 2.1
message Notification {
int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
Path prefix = 2; // Prefix used for paths in the message.
// An alias for the path specified in the prefix field.
// Reference: gNMI Specification Section 2.4.2
string alias = 3;
repeated Update update = 4; // Data elements that have changed values.
repeated Path delete = 5; // Data elements that have been deleted.
// This notification contains a set of paths that are always updated together
// referenced by a globally unique prefix.
bool atomic = 6;
}
// Update is a re-usable message that is used to store a particular Path,
// Value pair.
// Reference: gNMI Specification Section 2.1
message Update {
Path path = 1; // The path (key) for the update.
Value value = 2 [deprecated=true]; // The value (value) for the update.
TypedValue val = 3; // The explicitly typed update value.
uint32 duplicates = 4; // Number of coalesced duplicates.
}
// TypedValue is used to encode a value being sent between the client and
// target (originated by either entity).
message TypedValue {
// One of the fields within the val oneof is populated with the value
// of the update. The type of the value being included in the Update
// determines which field should be populated. In the case that the
// encoding is a particular form of the base protobuf type, a specific
// field is used to store the value (e.g., json_val).
oneof value {
string string_val = 1; // String value.
int64 int_val = 2; // Integer value.
uint64 uint_val = 3; // Unsigned integer value.
bool bool_val = 4; // Bool value.
bytes bytes_val = 5; // Arbitrary byte sequence value.
float float_val = 6; // Floating point value.
Decimal64 decimal_val = 7; // Decimal64 encoded value.
ScalarArray leaflist_val = 8; // Mixed type scalar array value.
google.protobuf.Any any_val = 9; // protobuf.Any encoded bytes.
bytes json_val = 10; // JSON-encoded text.
bytes json_ietf_val = 11; // JSON-encoded text per RFC7951.
string ascii_val = 12; // Arbitrary ASCII text.
// Protobuf binary encoded bytes. The message type is not included.
// See the specification at
// github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md
// for a complete specification.
bytes proto_bytes = 13;
}
}
// Path encodes a data tree path as a series of repeated strings, with
// each element of the path representing a data tree node name and the
// associated attributes.
// Reference: gNMI Specification Section 2.2.2.
message Path {
// Elements of the path are no longer encoded as a string, but rather within
// the elem field as a PathElem message.
repeated string element = 1 [deprecated=true];
string origin = 2; // Label to disambiguate path.
repeated PathElem elem = 3; // Elements of the path.
string target = 4; // The name of the target
// (Sec. 2.2.2.1)
}
// PathElem encodes an element of a gNMI path, along with any attributes (keys)
// that may be associated with it.
// Reference: gNMI Specification Section 2.2.2.
message PathElem {
string name = 1; // The name of the element in the path.
map<string, string> key = 2; // Map of key (attribute) name to value.
}
// Value encodes a data tree node's value - along with the way in which
// the value is encoded. This message is deprecated by gNMI 0.3.0.
// Reference: gNMI Specification Section 2.2.3.
message Value {
option deprecated = true;
bytes value = 1; // Value of the variable being transmitted.
Encoding type = 2; // Encoding used for the value field.
}
// Encoding defines the value encoding formats that are supported by the gNMI
// protocol. These encodings are used by both the client (when sending Set
// messages to modify the state of the target) and the target when serializing
// data to be returned to the client (in both Subscribe and Get RPCs).
// Reference: gNMI Specification Section 2.3
enum Encoding {
JSON = 0; // JSON encoded text.
BYTES = 1; // Arbitrarily encoded bytes.
PROTO = 2; // Encoded according to out-of-band agreed Protobuf.
ASCII = 3; // ASCII text of an out-of-band agreed format.
JSON_IETF = 4; // JSON encoded text as per RFC7951.
}
// Error message previously utilised to return errors to the client. Deprecated
// in favour of using the google.golang.org/genproto/googleapis/rpc/status
// message in the RPC response.
// Reference: gNMI Specification Section 2.5
message Error {
option deprecated = true;
uint32 code = 1; // Canonical gRPC error code.
string message = 2; // Human readable error.
google.protobuf.Any data = 3; // Optional additional information.
}
// Decimal64 is used to encode a fixed precision decimal number. The value
// is expressed as a set of digits with the precision specifying the
// number of digits following the decimal point in the digit set.
message Decimal64 {
int64 digits = 1; // Set of digits.
uint32 precision = 2; // Number of digits following the decimal point.
}
// ScalarArray is used to encode a mixed-type array of values.
message ScalarArray {
// The set of elements within the array. Each TypedValue message should
// specify only elements that have a field identifier of 1-7 (i.e., the
// values are scalar values).
repeated TypedValue element = 1;
}
// SubscribeRequest is the message sent by the client to the target when
// initiating a subscription to a set of paths within the data tree. The
// request field must be populated and the initial message must specify a
// SubscriptionList to initiate a subscription. The message is subsequently
// used to define aliases or trigger polled data to be sent by the target.
// Reference: gNMI Specification Section 3.5.1.1
message SubscribeRequest {
oneof request {
SubscriptionList subscribe = 1; // Specify the paths within a subscription.
Poll poll = 3; // Trigger a polled update.
AliasList aliases = 4; // Aliases to be created.
}
// Extension messages associated with the SubscribeRequest. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 5;
}
// Poll is sent within a SubscribeRequest to trigger the device to
// send telemetry updates for the paths that are associated with the
// subscription.
// Reference: gNMI Specification Section Section 3.5.1.4
message Poll {
}
// SubscribeResponse is the message used by the target within a Subscribe RPC.
// The target includes a Notification message which is used to transmit values
// of the path(s) that are associated with the subscription. The same message
// is to indicate that the target has sent all data values once (is
// synchronized).
// Reference: gNMI Specification Section 3.5.1.4
message SubscribeResponse {
oneof response {
Notification update = 1; // Changed or sampled value for a path.
// Indicate target has sent all values associated with the subscription
// at least once.
bool sync_response = 3;
// Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status
Error error = 4 [deprecated=true];
}
// Extension messages associated with the SubscribeResponse. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 5;
}
// SubscriptionList is used within a Subscribe message to specify the list of
// paths that the client wishes to subscribe to. The message consists of a
// list of (possibly prefixed) paths, and options that relate to the
// subscription.
// Reference: gNMI Specification Section 3.5.1.2
message SubscriptionList {
Path prefix = 1; // Prefix used for paths.
repeated Subscription subscription = 2; // Set of subscriptions to create.
// Whether target defined aliases are allowed within the subscription.
bool use_aliases = 3;
QOSMarking qos = 4; // DSCP marking to be used.
// Mode of the subscription.
enum Mode {
STREAM = 0; // Values streamed by the target (Sec. 3.5.1.5.2).
ONCE = 1; // Values sent once-off by the target (Sec. 3.5.1.5.1).
POLL = 2; // Values sent in response to a poll request (Sec. 3.5.1.5.3).
}
Mode mode = 5;
// Whether elements of the schema that are marked as eligible for aggregation
// should be aggregated or not.
bool allow_aggregation = 6;
// The set of schemas that define the elements of the data tree that should
// be sent by the target.
repeated ModelData use_models = 7;
// The encoding that the target should use within the Notifications generated
// corresponding to the SubscriptionList.
Encoding encoding = 8;
// An optional field to specify that only updates to current state should be
// sent to a client. If set, the initial state is not sent to the client but
// rather only the sync message followed by any subsequent updates to the
// current state. For ONCE and POLL modes, this causes the server to send only
// the sync message (Sec. 3.5.2.3).
bool updates_only = 9;
}
// Subscription is a single request within a SubscriptionList. The path
// specified is interpreted (along with the prefix) as the elements of the data
// tree that the client is subscribing to. The mode determines how the target
// should trigger updates to be sent.
// Reference: gNMI Specification Section 3.5.1.3
message Subscription {
Path path = 1; // The data tree path.
SubscriptionMode mode = 2; // Subscription mode to be used.
uint64 sample_interval = 3; // ns between samples in SAMPLE mode.
// Indicates whether values that have not changed should be sent in a SAMPLE
// subscription.
bool suppress_redundant = 4;
// Specifies the maximum allowable silent period in nanoseconds when
// suppress_redundant is in use. The target should send a value at least once
// in the period specified.
uint64 heartbeat_interval = 5;
}
// SubscriptionMode is the mode of the subscription, specifying how the
// target must return values in a subscription.
// Reference: gNMI Specification Section 3.5.1.3
enum SubscriptionMode {
TARGET_DEFINED = 0; // The target selects the relevant mode for each element.
ON_CHANGE = 1; // The target sends an update on element value change.
SAMPLE = 2; // The target samples values according to the interval.
}
// QOSMarking specifies the DSCP value to be set on transmitted telemetry
// updates from the target.
// Reference: gNMI Specification Section 3.5.1.2
message QOSMarking {
uint32 marking = 1;
}
// Alias specifies a data tree path, and an associated string which defines an
// alias which is to be used for this path in the context of the RPC. The alias
// is specified as a string which is prefixed with "#" to disambiguate it from
// data tree element paths.
// Reference: gNMI Specification Section 2.4.2
message Alias {
Path path = 1; // The path to be aliased.
string alias = 2; // The alias value, a string prefixed by "#".
}
// AliasList specifies a list of aliases. It is used in a SubscribeRequest for
// a client to create a set of aliases that the target is to utilize.
// Reference: gNMI Specification Section 3.5.1.6
message AliasList {
repeated Alias alias = 1; // The set of aliases to be created.
}
// SetRequest is sent from a client to the target to update values in the data
// tree. Paths are either deleted by the client, or modified by means of being
// updated, or replaced. Where a replace is used, unspecified values are
// considered to be replaced, whereas when update is used the changes are
// considered to be incremental. The set of changes that are specified within
// a single SetRequest are considered to be a transaction.
// Reference: gNMI Specification Section 3.4.1
message SetRequest {
Path prefix = 1; // Prefix used for paths in the message.
repeated Path delete = 2; // Paths to be deleted from the data tree.
repeated Update replace = 3; // Updates specifying elements to be replaced.
repeated Update update = 4; // Updates specifying elements to updated.
// Extension messages associated with the SetRequest. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 5;
}
// SetResponse is the response to a SetRequest, sent from the target to the
// client. It reports the result of the modifications to the data tree that were
// specified by the client. Errors for this RPC should be reported using the
// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto
// message in the RPC return. The gnmi.Error message can be used to add additional
// details where required.
// Reference: gNMI Specification Section 3.4.2
message SetResponse {
Path prefix = 1; // Prefix used for paths.
// A set of responses specifying the result of the operations specified in
// the SetRequest.
repeated UpdateResult response = 2;
Error message = 3 [deprecated=true]; // The overall status of the transaction.
int64 timestamp = 4; // Timestamp of transaction (ns since epoch).
// Extension messages associated with the SetResponse. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 5;
}
// UpdateResult is used within the SetResponse message to communicate the
// result of an operation specified within a SetRequest message.
// Reference: gNMI Specification Section 3.4.2
message UpdateResult {
// The operation that was associated with the Path specified.
enum Operation {
INVALID = 0;
DELETE = 1; // The result relates to a delete of Path.
REPLACE = 2; // The result relates to a replace of Path.
UPDATE = 3; // The result relates to an update of Path.
}
// Deprecated timestamp for the UpdateResult, this field has been
// replaced by the timestamp within the SetResponse message, since
// all mutations effected by a set should be applied as a single
// transaction.
int64 timestamp = 1 [deprecated=true];
Path path = 2; // Path associated with the update.
Error message = 3 [deprecated=true]; // Status of the update operation.
Operation op = 4; // Update operation type.
}
// GetRequest is sent when a client initiates a Get RPC. It is used to specify
// the set of data elements for which the target should return a snapshot of
// data. The use_models field specifies the set of schema modules that are to
// be used by the target - where use_models is not specified then the target
// must use all schema models that it has.
// Reference: gNMI Specification Section 3.3.1
message GetRequest {
Path prefix = 1; // Prefix used for paths.
repeated Path path = 2; // Paths requested by the client.
// Type of elements within the data tree.
enum DataType {
ALL = 0; // All data elements.
CONFIG = 1; // Config (rw) only elements.
STATE = 2; // State (ro) only elements.
// Data elements marked in the schema as operational. This refers to data
// elements whose value relates to the state of processes or interactions
// running on the device.
OPERATIONAL = 3;
}
DataType type = 3; // The type of data being requested.
Encoding encoding = 5; // Encoding to be used.
repeated ModelData use_models = 6; // The schema models to be used.
// Extension messages associated with the GetRequest. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 7;
}
// GetResponse is used by the target to respond to a GetRequest from a client.
// The set of Notifications corresponds to the data values that are requested
// by the client in the GetRequest.
// Reference: gNMI Specification Section 3.3.2
message GetResponse {
repeated Notification notification = 1; // Data values.
Error error = 2 [deprecated=true]; // Errors that occurred in the Get.
// Extension messages associated with the GetResponse. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 3;
}
// CapabilityRequest is sent by the client in the Capabilities RPC to request
// that the target reports its capabilities.
// Reference: gNMI Specification Section 3.2.1
message CapabilityRequest {
// Extension messages associated with the CapabilityRequest. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 1;
}
// CapabilityResponse is used by the target to report its capabilities to the
// client within the Capabilities RPC.
// Reference: gNMI Specification Section 3.2.2
message CapabilityResponse {
repeated ModelData supported_models = 1; // Supported schema models.
repeated Encoding supported_encodings = 2; // Supported encodings.
string gNMI_version = 3; // Supported gNMI version.
// Extension messages associated with the CapabilityResponse. See the
// gNMI extension specification for further definition.
repeated gnmi_ext.Extension extension = 4;
}
// ModelData is used to describe a set of schema modules. It can be used in a
// CapabilityResponse where a target reports the set of modules that it
// supports, and within the SubscribeRequest and GetRequest messages to specify
// the set of models from which data tree elements should be reported.
// Reference: gNMI Specification Section 3.2.3
message ModelData {
string name = 1; // Name of the model.
string organization = 2; // Organization publishing the model.
string version = 3; // Semantic version of the model.
}

View File

@@ -0,0 +1,89 @@
//
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
syntax = "proto3";
// Package gnmi_ext defines a set of extensions messages which can be optionally
// included with the request and response messages of gNMI RPCs. A set of
// well-known extensions are defined within this file, along with a registry for
// extensions defined outside of this package.
package gnmi_ext;
// The Extension message contains a single gNMI extension.
message Extension {
oneof ext {
RegisteredExtension registered_ext = 1; // A registered extension.
// Well known extensions.
MasterArbitration master_arbitration = 2; // Master arbitration extension.
History history = 3; // History extension.
}
}
// The RegisteredExtension message defines an extension which is defined outside
// of this file.
message RegisteredExtension {
ExtensionID id = 1; // The unique ID assigned to this extension.
bytes msg = 2; // The binary-marshalled protobuf extension payload.
}
// RegisteredExtension is an enumeration acting as a registry for extensions
// defined by external sources.
enum ExtensionID {
EID_UNSET = 0;
// New extensions are to be defined within this enumeration - their definition
// MUST link to a reference describing their implementation.
// An experimental extension that may be used during prototyping of a new
// extension.
EID_EXPERIMENTAL = 999;
}
// MasterArbitration is used to select the master among multiple gNMI clients
// with the same Roles. The client with the largest election_id is honored as
// the master.
// The document about gNMI master arbitration can be found at
// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md
message MasterArbitration {
Role role = 1;
Uint128 election_id = 2;
}
// Representation of unsigned 128-bit integer.
message Uint128 {
uint64 high = 1;
uint64 low = 2;
}
// There can be one master for each role. The role is identified by its id.
message Role {
string id = 1;
// More fields can be added if needed, for example, to specify what paths the
// role can read/write.
}
// The History extension allows clients to request historical data. Its
// spec can be found at
// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md
message History {
oneof request {
int64 snapshot_time = 1; // Nanoseconds since the epoch
TimeRange range = 2;
}
}
message TimeRange {
int64 start = 1; // Nanoseconds since the epoch
int64 end = 2; // Nanoseconds since the epoch
}

View File

@@ -0,0 +1,315 @@
syntax = "proto3";
package gnoi.sonic;
//option (types.gnoi_version) = "0.1.0";
service SonicService {
rpc ShowTechsupport (TechsupportRequest) returns (TechsupportResponse) {}
rpc ShowTechsupportCancel (TechsupportCancelRequest) returns (TechsupportCancelResponse) {}
rpc Sum (SumRequest) returns (SumResponse) {}
rpc ImageInstall(ImageInstallRequest) returns (ImageInstallResponse) {}
rpc ImageRemove(ImageRemoveRequest) returns (ImageRemoveResponse) {}
rpc ImageDefault(ImageDefaultRequest) returns (ImageDefaultResponse) {}
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {}
rpc Refresh(RefreshRequest) returns (RefreshResponse) {}
rpc ClearNeighbors(ClearNeighborsRequest) returns (ClearNeighborsResponse) {}
rpc VlanReplace(VlanReplaceRequest) returns (VlanReplaceResponse) {}
rpc GetAuditLog (GetAuditLogRequest) returns (GetAuditLogResponse) {}
rpc ClearAuditLog (ClearAuditLogRequest) returns (ClearAuditLogResponse) {}
rpc ShowSysLog(ShowSysLogRequest) returns (GetShowSysLogResponse) {}
rpc GetEvents (GetEventsRequest) returns (GetEventsResponse) {}
rpc GetAlarms (GetAlarmsRequest) returns (GetAlarmsResponse) {}
rpc AckAlarms (AckAlarmsRequest) returns (AckAlarmsResponse) {}
rpc UnackAlarms (UnackAlarmsRequest) returns (UnackAlarmsResponse) {}
rpc GetEventProfile (GetEventProfileRequest) returns (GetEventProfileResponse) {}
rpc SetEventProfile (SetEventProfileRequest) returns (SetEventProfileResponse) {}
}
message SonicOutput {
int32 status = 1;
string status_detail = 2;
}
message GetEventProfileRequest {
}
message GetEventProfileResponse {
message Output {
string file_name = 1;
repeated string file_list = 2;
}
Output output = 1;
}
message SetEventProfileRequest {
message Input {
string filename =1;
}
Input input =1;
}
message SetEventProfileResponse {
SonicOutput output = 1;
}
message AckAlarmsRequest {
message Input {
repeated string id = 1;
}
Input input = 1;
}
message AckAlarmsResponse {
SonicOutput output = 1;
}
message UnackAlarmsRequest {
message Input {
repeated string id = 1;
}
Input input = 1;
}
message UnackAlarmsResponse {
SonicOutput output = 1;
}
message EventTimeFilter {
string begin = 1;
string end = 2;
}
message EventId {
string begin = 1;
string end = 2;
}
message EventsFilter {
EventTimeFilter time = 1;
string interval = 2;
string severity = 3;
EventId id = 4;
}
message GetEventsRequest {
EventsFilter input = 1;
}
message Event {
string id = 1;
string resource = 2;
string text = 3;
string time_created = 4;
string type_id = 5;
string severity = 6;
string action = 7;
}
message Events {
repeated Event EVENT_LIST = 1;
}
message EventsResponse {
int32 status = 1;
string status_detail = 2;
Events EVENT =3;
}
message GetEventsResponse {
EventsResponse output = 1;
}
message Alarm {
string id = 1;
string resource = 2;
string text = 3;
string time_created = 4;
string type_id = 5;
string severity = 6;
bool acknowledged = 7;
string acknowledge_time = 8;
}
message GetAlarmsRequest {
EventsFilter input = 1;
}
message Alarms {
repeated Alarm ALARM_LIST = 1;
}
message AlarmsResponse {
int32 status = 1;
string status_detail = 2;
Alarms ALARM =3;
}
message GetAlarmsResponse {
AlarmsResponse output = 1;
}
message TechsupportRequest {
message Input {
string date = 1;
}
Input input = 1;
}
message TechsupportResponse {
message Output {
uint32 status = 1;
string status_detail = 2;
string output_filename = 3;
}
Output output = 1;
}
message TechsupportCancelRequest {
}
message TechsupportCancelResponse {
message Output {
uint32 status = 1;
string status_detail = 2;
}
Output output = 1;
}
message ClearNeighborsRequest {
message Input {
bool force = 1;
string family = 2;
string ip = 3;
string ifname = 4;
}
Input input = 1;
}
message ClearNeighborsResponse {
message Output {
string response = 1;
}
Output output = 1;
}
message VlanReplaceRequest {
message Input {
string ifname = 1;
string vlanlist = 2;
}
Input input = 1;
}
message VlanReplaceResponse {
message Output {
string response = 1;
}
Output output = 1;
}
message SumRequest {
message Input {
int32 left = 1;
int32 right = 2;
}
Input input = 1;
}
message SumResponse {
message Output {
int32 result = 1;
}
Output output = 1;
}
message ImageInstallRequest {
message Input {
string imagename = 1;
}
Input input = 1;
}
message ImageInstallResponse {
SonicOutput output = 1;
}
message ImageRemoveRequest {
message Input {
string imagename = 1;
}
Input input = 1;
}
message ImageRemoveResponse {
SonicOutput output = 1;
}
message ImageDefaultRequest {
message Input {
string imagename = 1;
}
Input input = 1;
}
message ImageDefaultResponse {
SonicOutput output = 1;
}
message GetAuditLogRequest {
message Input {
string content_type = 1;
}
Input input = 1;
}
message GetAuditLogResponse {
message AuditOutput {
repeated string audit_content = 1;
}
AuditOutput output = 1;
}
message ClearAuditLogRequest {
}
message ClearAuditLogResponse {
SonicOutput output = 1;
}
message ShowSysLogRequest {
message Input {
int32 num_lines = 1 ;
}
Input input = 1;
}
message ShowSysLogResponse {
repeated string status_detail = 1;
}
message GetShowSysLogResponse {
ShowSysLogResponse output = 1;
}
message JwtToken {
string access_token = 1;
string type = 2;
int64 expires_in = 3;
}
message AuthenticateRequest {
string username = 1;
string password = 2;
}
message AuthenticateResponse {
JwtToken Token = 1;
}
message RefreshRequest {
}
message RefreshResponse {
JwtToken Token = 1;
}

View File

@@ -0,0 +1,339 @@
//
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Generic Network Operation Interface, GNOI, defines a set of RPC's used for
// the operational aspects of network targets. These services are meant to be
// used in conjunction with GNMI for all target state and operational aspects
// of a network target. The gnoi.system.Service is the only mandatory vendor
// implementation.
syntax = "proto3";
package gnoi.system;
import "common.proto";
import "types.proto";
option (types.gnoi_version) = "1.0.0";
// The gNOI service is a collection of operational RPC's that allow for the
// management of a target outside of the configuration and telemetry pipeline.
service System {
// Ping executes the ping command on the target and streams back
// the results. Some targets may not stream any results until all
// results are in. The stream should provide single ping packet responses
// and must provide summary statistics.
rpc Ping(PingRequest) returns (stream PingResponse) {}
// Traceroute executes the traceroute command on the target and streams back
// the results. Some targets may not stream any results until all
// results are in. If a hop count is not explicitly provided,
// 30 is used.
rpc Traceroute(TracerouteRequest) returns (stream TracerouteResponse) {}
// Time returns the current time on the target. Time is typically used to
// test if a target is actually responding.
rpc Time(TimeRequest) returns (TimeResponse) {}
// SetPackage places a software package (possibly including bootable images)
// on the target. The file is sent in sequential messages, each message
// up to 64KB of data. A final message must be sent that includes the hash
// of the data sent. An error is returned if the location does not exist or
// there is an error writing the data. If no checksum is received, the target
// must assume the operation is incomplete and remove the partially
// transmitted file. The target should initially write the file to a temporary
// location so a failure does not destroy the original file.
rpc SetPackage(stream SetPackageRequest) returns (SetPackageResponse) {}
// SwitchControlProcessor will switch from the current route processor to the
// provided route processor. If the current route processor is the same as the
// one provided it is a NOOP. If the target does not exist an error is
// returned.
rpc SwitchControlProcessor(SwitchControlProcessorRequest)
returns (SwitchControlProcessorResponse) {}
// Reboot causes the target to reboot, possibly at some point in the future.
// If the method of reboot is not supported then the Reboot RPC will fail.
// If the reboot is immediate the command will block until the subcomponents
// have restarted.
// If a reboot on the active control processor is pending the service must
// reject all other reboot requests.
// If a reboot request for active control processor is initiated with other
// pending reboot requests it must be rejected.
rpc Reboot(RebootRequest) returns (RebootResponse) {}
// RebootStatus returns the status of reboot for the target.
rpc RebootStatus(RebootStatusRequest) returns (RebootStatusResponse) {}
// CancelReboot cancels any pending reboot request.
rpc CancelReboot(CancelRebootRequest) returns (CancelRebootResponse) {}
// KillProcess kills an OS process and optionally restarts it.
rpc KillProcess(KillProcessRequest) returns (KillProcessResponse) {}
// TODO(hines): Add RotateCertificate workflow
// TODO(hines): Add SetSSHPrivateKey
}
message SwitchControlProcessorRequest {
types.Path control_processor = 1;
}
message SwitchControlProcessorResponse {
types.Path control_processor = 1;
string version = 2; // Current software version.
int64 uptime = 3; // Uptime in nanoseconds since epoch.
}
// A RebootRequest requests the specified target be rebooted using the specified
// method after the specified delay. Only the DEFAULT method with a delay of 0
// is guaranteed to be accepted for all target types.
message RebootRequest {
RebootMethod method = 1;
// Delay in nanoseconds before issuing reboot.
uint64 delay = 2;
// Informational reason for the reboot.
string message = 3;
// Optional sub-components to reboot.
repeated types.Path subcomponents = 4;
// Force reboot if sanity checks fail. (ex. uncommited configuration)
bool force = 5;
}
message RebootResponse {
}
// A RebootMethod determines what should be done with a target when a Reboot is
// requested. Only the COLD method is required to be supported by all
// targets. Methods the target does not support should result in failure.
//
// It is vendor defined if a WARM reboot is the same as an NSF reboot.
enum RebootMethod {
UNKNOWN = 0; // Invalid default method.
COLD = 1; // Shutdown and restart OS and all hardware.
POWERDOWN = 2; // Halt and power down, if possible.
HALT = 3; // Halt, if possible.
WARM = 4; // Reload configuration but not underlying hardware.
NSF = 5; // Non-stop-forwarding reboot, if possible.
// RESET method is deprecated in favor of the gNOI FactoryReset.Start().
reserved 6;
POWERUP = 7; // Apply power, no-op if power is already on.
}
// A CancelRebootRequest requests the cancelation of any outstanding reboot
// request.
message CancelRebootRequest {
string message = 1; // informational reason for the cancel
repeated types.Path subcomponents = 2; // optional sub-components.
}
message CancelRebootResponse {
}
message RebootStatusRequest {
repeated types.Path subcomponents = 1; // optional sub-component.
}
message RebootStatusResponse {
bool active = 1; // If reboot is active.
uint64 wait = 2; // Time left until reboot.
uint64 when = 3; // Time to reboot in nanoseconds since the epoch.
string reason = 4; // Reason for reboot.
uint32 count = 5; // Number of reboots since active.
}
// A TimeRequest requests the current time accodring to the target.
message TimeRequest {
}
message TimeResponse {
uint64 time = 1; // Current time in nanoseconds since epoch.
}
// A PingRequest describes the ping operation to perform. Only the destination
// fields is required. Any field not specified is set to a reasonable server
// specified value. Not all fields are supported by all vendors.
//
// A count of 0 defaults to a vendor specified value, typically 5. A count of
// -1 means continue until the RPC times out or is canceled.
//
// If the interval is -1 then a flood ping is issued.
//
// If the size is 0, the vendor default size will be used (typically 56 bytes).
message PingRequest {
string destination = 1; // Destination address to ping. required.
string source = 2; // Source address to ping from.
int32 count = 3; // Number of packets.
int64 interval = 4; // Nanoseconds between requests.
int64 wait = 5; // Nanoseconds to wait for a response.
int32 size = 6; // Size of request packet. (excluding ICMP header)
bool do_not_fragment = 7; // Set the do not fragment bit. (IPv4 destinations)
bool do_not_resolve = 8; // Do not try resolve the address returned.
types.L3Protocol l3protocol = 9; // Layer3 protocol requested for the ping.
}
// A PingResponse represents either the reponse to a single ping packet
// (the bytes field is non-zero) or the summary statistics (sent is non-zero).
//
// For a single ping packet, time is the round trip time, in nanoseconds. For
// summary statistics, it is the time spent by the ping operation. The time is
// not always present in summary statistics. The std_dev is not always present
// in summary statistics.
message PingResponse {
string source = 1; // Source of received bytes.
int64 time = 2;
int32 sent = 3; // Total packets sent.
int32 received = 4; // Total packets received.
int64 min_time = 5; // Minimum round trip time in nanoseconds.
int64 avg_time = 6; // Average round trip time in nanoseconds.
int64 max_time = 7; // Maximum round trip time in nanoseconds.
int64 std_dev = 8; // Standard deviation in round trip time.
int32 bytes = 11; // Bytes received.
int32 sequence = 12; // Sequence number of received packet.
int32 ttl = 13; // Remaining time to live value.
}
// A TracerouteRequest describes the traceroute operation to perform. Only the
// destination field is required. Any field not specified is set to a
// reasonable server specified value. Not all fields are supported by all
// vendors.
//
// If the hop_count is -1 the traceroute will continue forever.
//
message TracerouteRequest {
string source = 1; // Source address to ping from.
string destination = 2; // Destination address to ping.
uint32 initial_ttl = 3; // Initial TTL. (default=1)
int32 max_ttl = 4; // Maximum number of hops. (default=30)
int64 wait = 5; // Nanoseconds to wait for a response.
bool do_not_fragment = 6; // Set the do not fragment bit. (IPv4 destinations)
bool do_not_resolve = 7; // Do not try resolve the address returned.
types.L3Protocol l3protocol = 8; // Layer-3 protocol requested for the ping.
enum L4Protocol {
ICMP = 0; // Use ICMP ECHO for probes.
TCP = 1; // Use TCP SYN for probes.
UDP = 2; // Use UDP for probes.
}
L4Protocol l4protocol = 9;
bool do_not_lookup_asn = 10; // Do not try to lookup ASN
}
// A TraceRouteResponse contains the result of a single traceoute packet.
//
// There may be an optional initial response that provides information about the
// traceroute request itself and contains at least one of the fields in the the
// initial block of fields and none of the fields following that block. All
// subsequent responses should not contain any of these fields.
//
// Typically multiple responses are received for each hop, as the packets are
// received.
//
// The mpls field maps names to values. Example names include "Label", "CoS",
// "TTL", "S", and "MRU".
// [Perhaps we should list the canonical names that must be used when
// applicable].
message TracerouteResponse {
// The following fields are only filled in for the first message.
// If any of these fields are specified, all fields following this
// block are left unspecified.
string destination_name = 1;
string destination_address = 2;
int32 hops = 3;
int32 packet_size = 4;
// State is the resulting state of a single traceoroute packet.
enum State {
DEFAULT = 0; // Normal hop response.
NONE = 1; // No response.
UNKNOWN = 2; // Unknown response state.
ICMP = 3; // See icmp_code field.
HOST_UNREACHABLE = 4; // Host unreachable.
NETWORK_UNREACHABLE = 5; // Network unreachable.
PROTOCOL_UNREACHABLE = 6; // Protocol unreachable.
SOURCE_ROUTE_FAILED = 7; // Source route failed.
FRAGMENTATION_NEEDED = 8; // Fragmentation needed.
PROHIBITED = 9; // Communication administratively prohibited.
PRECEDENCE_VIOLATION = 10; // Host precedence violation.
PRECEDENCE_CUTOFF = 11; // Precedence cutoff in effect.
}
// The following fields provide the disposition of a single traceroute
// packet.
int32 hop = 5; // Hop number. required.
string address = 6; // Address of responding hop. required.
string name = 7; // Name of responding hop.
int64 rtt = 8; // Round trip time in nanoseconds.
State state = 9; // State of this hop.
int32 icmp_code = 10; // Code terminating hop.
map<string, string> mpls = 11; // MPLS key/value pairs.
repeated int32 as_path = 12; // AS path.
}
// Package defines a single package file to be placed on the target.
message Package {
// Destination path and filename of the package.
string filename = 1;
// Version of the package. (vendor internal name)
string version = 4;
// Indicates that the package should be made active after receipt on
// the device. For system image packages, the new image is expected to
// be active after a reboot.
bool activate = 5;
// Details for the device to download the package from a remote location.
common.RemoteDownload remote_download = 6;
}
// SetPackageRequest will place the package onto the target and optionally mark
// it as the next bootable image. The initial message must be a package
// message containing the filename and information about the file. Following the
// initial message the contents are then streamed in maximum 64k chunks. The
// final message must be a hash message contains the hash of the file contents.
message SetPackageRequest {
oneof request {
Package package = 1;
bytes contents = 2;
types.HashType hash = 3; // Verification hash of data.
}
}
message SetPackageResponse {
}
// KillProcessRequest describes the process kill operation. Either a pid or
// process name must be specified, and a termination signal must be specified.
message KillProcessRequest {
// Process ID of the process to be killed.
uint32 pid = 1;
// Name of the process to be killed.
string name = 2;
// Termination signal sent to the process.
enum Signal {
SIGNAL_UNSPECIFIED = 0; // Invalid default.
SIGNAL_TERM = 1; // Terminate the process gracefully.
SIGNAL_KILL = 2; // Terminate the process immediately.
SIGNAL_HUP = 3; // Reload the process configuration.
}
Signal signal = 3;
// Whether the process should be restarted after termination.
// This value is ignored when the termination signal is SIGHUP.
bool restart = 4;
}
// KillProcessResponse describes the result of the process kill operation.
message KillProcessResponse {
}

View File

@@ -0,0 +1,75 @@
//
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
syntax = "proto3";
package gnoi.types;
import "google/protobuf/descriptor.proto";
option go_package = "github.com/openconfig/gnoi/types";
// Define a protobuf FileOption that defines the gNOI service version.
extend google.protobuf.FileOptions {
// The gNOI service semantic version.
string gnoi_version = 1002;
}
// Generic Layer 3 Protocol enumeration.
enum L3Protocol {
UNSPECIFIED = 0;
IPV4 = 1;
IPV6 = 2;
}
// HashType defines the valid hash methods for data verification. UNSPECIFIED
// should be treated an error.
message HashType {
enum HashMethod {
UNSPECIFIED = 0;
SHA256 = 1;
SHA512 = 2;
MD5 = 3;
}
HashMethod method = 1;
bytes hash = 2;
}
// Path encodes a data tree path as a series of repeated strings, with
// each element of the path representing a data tree node name and the
// associated attributes.
// Reference: gNMI Specification Section 2.2.2.
message Path {
string origin = 2; // Label to disambiguate path.
repeated PathElem elem = 3; // Elements of the path.
}
// PathElem encodes an element of a gNMI path, along with any attributes (keys)
// that may be associated with it.
// Reference: gNMI Specification Section 2.2.2.
message PathElem {
string name = 1; // The name of the element in the path.
map<string, string> key = 2; // Map of key (attribute) name to value.
}
// Credentials defines credentials needed to perform authentication on a device.
message Credentials {
string username = 1;
oneof password {
string cleartext = 2;
HashType hashed = 3;
}
}

View File

@@ -0,0 +1,408 @@
#include <route.hpp>
#include <sai_redis.hpp>
#include <state.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#include <sw/redis++/redis++.h>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <router-utils.h>
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <arpa/inet.h>
#include <algorithm> // std::find_if
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iterator> // std::inserter
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using nlohmann::json;
namespace larch {
static std::string fib_key_to_str(const ucentral_router_fib_key &fib_key)
{
std::array<char, INET_ADDRSTRLEN + 1> ip_buf{};
if (!inet_ntop(AF_INET, &fib_key.prefix, ip_buf.data(), ip_buf.size()))
throw std::runtime_error{
"Failed to convert FIB prefix from binary to text form"};
std::string ip{ip_buf.data()};
ip += "/" + std::to_string(fib_key.prefix_len);
return ip;
}
void create_route(
std::uint16_t router_id,
const ucentral_router_fib_key &fib_key,
const ucentral_router_fib_info &fib_info)
{
// VRF is not supported
if (router_id != 0)
return;
json route_json;
route_json["prefix"] = fib_key_to_str(fib_key);
route_json["vrf-name"] = "default";
switch (fib_info.type)
{
case ucentral_router_fib_info::UCENTRAL_ROUTE_BLACKHOLE:
{
route_json["blackhole"] = "true,false";
break;
}
case ucentral_router_fib_info::UCENTRAL_ROUTE_CONNECTED:
{
route_json["ifname"] =
"Vlan" + std::to_string(fib_info.connected.vid);
break;
}
case ucentral_router_fib_info::UCENTRAL_ROUTE_NH:
{
route_json["ifname"] =
"Vlan" + std::to_string(fib_info.nh.vid);
std::array<char, INET_ADDRSTRLEN + 1> ip_buf{};
if (!inet_ntop(
AF_INET,
&fib_info.nh.gw,
ip_buf.data(),
ip_buf.size()))
{
throw std::runtime_error{
"Failed to convert gateway address from "
"binary to text form"};
}
break;
}
default:
{
return;
}
}
json add_route_json;
add_route_json["sonic-static-route:sonic-static-route"]["STATIC_ROUTE"]
["STATIC_ROUTE_LIST"] = {route_json};
gnmi_set(
"/sonic-static-route:sonic-static-route/",
add_route_json.dump());
}
void remove_route(
std::uint16_t router_id,
const ucentral_router_fib_key &fib_key)
{
// VRF is not supported
if (router_id != 0)
return;
gnmi_operation op;
op.add_delete(
"/sonic-static-route:sonic-static-route/STATIC_ROUTE/"
"STATIC_ROUTE_LIST[prefix="
+ fib_key_to_str(fib_key) + "][vrf-name=default]");
op.execute();
}
std::vector<ucentral_router_fib_node> get_routes(std::uint16_t router_id)
{
// VRF is not supported
if (router_id != 0)
return {};
const json static_routes_json =
json::parse(gnmi_get("/sonic-static-route:sonic-static-route/"
"STATIC_ROUTE/STATIC_ROUTE_LIST"));
std::vector<ucentral_router_fib_node> routes;
for (const auto &route_json : static_routes_json.value(
"sonic-static-route:STATIC_ROUTE_LIST",
json::array()))
{
if (route_json.contains("vrf-name")
&& route_json.at("vrf-name").template get<std::string>()
!= "default")
{
continue;
}
if (!route_json.contains("prefix"))
continue;
ucentral_router_fib_info fib_info{};
// For now only blackhole is supported
if (route_json.contains("blackhole"))
fib_info.type =
ucentral_router_fib_info::UCENTRAL_ROUTE_BLACKHOLE;
else
continue;
ucentral_router_fib_key fib_key{};
const int ret = inet_net_pton(
AF_INET,
route_json.at("prefix").template get<std::string>().c_str(),
&fib_key.prefix,
sizeof(fib_key.prefix));
if (ret == -1)
continue;
fib_key.prefix_len = ret;
routes.push_back({fib_key, fib_info});
}
return routes;
}
struct router_interface {
std::string mac;
sai::object_id port_oid;
};
static std::optional<router_interface>
parse_router_interface(const sai::object_id &oid)
{
router_interface router_if{};
std::unordered_map<std::string, std::string> entry;
state->redis_asic->hgetall(
"ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:" + oid,
std::inserter(entry, entry.begin()));
try
{
if (entry.at("SAI_ROUTER_INTERFACE_ATTR_TYPE")
!= "SAI_ROUTER_INTERFACE_TYPE_PORT")
{
// Other types are not supported
return std::nullopt;
}
router_if.mac =
entry.at("SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS");
router_if.port_oid =
entry.at("SAI_ROUTER_INTERFACE_ATTR_PORT_ID");
}
catch (const std::out_of_range &ex)
{
return std::nullopt;
}
return router_if;
}
std::vector<plat_gw_address> get_gw_addresses()
{
// TO-DO: remove this and use state->interfaces_addrs after mergin
// interface-addresses PR
std::vector<plat_ipv4> interfaces_addrs;
const auto port_name_mapping = sai::get_port_name_mapping();
std::vector<plat_gw_address> gw_addresses;
std::int64_t cursor = 0;
std::unordered_set<std::string> keys;
do
{
constexpr std::string_view pattern =
"ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY:*";
keys.clear();
cursor = state->redis_asic->scan(
cursor,
pattern,
std::inserter(keys, keys.begin()));
for (const auto &key : keys)
{
const json route_json =
json::parse(key.substr(pattern.size() - 1));
plat_gw_address gw_addr{};
// Get IP
cidr gw_ip_range = parse_cidr(
route_json.at("dest").template get<std::string>());
if (inet_pton(
AF_INET,
gw_ip_range.ip_address.c_str(),
&gw_addr.ip)
!= 1)
{
UC_LOG_ERR(
"Failed to parse GW IP address %s",
gw_ip_range.ip_address.c_str());
continue;
}
const auto *static_routes_begin = state->router.arr;
const auto *static_routes_end =
static_routes_begin + state->router.len;
if (std::find_if(
static_routes_begin,
static_routes_end,
[&gw_addr](const auto &fib_node) {
// TO-DO: uncomment after merging
// interface-addresses PR return
// addr_to_str(fib_node.key.prefix)
// == addr_to_str(gw_addr.ip);
return false;
})
!= static_routes_end)
{
continue;
}
if (std::find_if(
interfaces_addrs.cbegin(),
interfaces_addrs.cend(),
[&gw_addr](const auto &interface_addr) {
// TO-DO: uncomment after merging
// interface-addresses PR return
// addr_to_str(interface_addr.subnet)
// == addr_to_str(gw_addr.ip);
return false;
})
!= interfaces_addrs.cend())
{
continue;
}
std::unordered_map<std::string, std::string> entry;
state->redis_asic->hgetall(
key,
std::inserter(entry, entry.begin()));
const auto router_it =
entry.find("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID");
if (router_it == entry.cend())
continue;
auto router_if_opt =
parse_router_interface(router_it->second);
if (!router_if_opt)
continue;
// Get MAC
std::strncpy(
gw_addr.mac,
router_if_opt->mac.c_str(),
std::size(gw_addr.mac) - 1);
// Get port name
const auto port_name_it =
port_name_mapping.find(router_if_opt->port_oid);
if (port_name_it == port_name_mapping.cend())
continue;
std::strncpy(
gw_addr.port,
port_name_it->second.c_str(),
std::size(gw_addr.port) - 1);
gw_addresses.push_back(gw_addr);
}
} while (cursor != 0);
return gw_addresses;
}
void apply_route_config(plat_cfg *cfg)
{
ucentral_router old_router{}, new_router{};
// Save the old router
old_router = state->router;
// Load new router, this also does the necessary allocations
if (ucentral_router_fib_db_copy(&cfg->router, &new_router) != 0)
throw std::runtime_error{"Failed to copy FIB DB"};
if (!old_router.sorted)
ucentral_router_fib_db_sort(&old_router);
if (!new_router.sorted)
ucentral_router_fib_db_sort(&new_router);
std::size_t old_idx = 0, new_idx = 0;
int diff = 0;
for_router_db_diff(&new_router, &old_router, new_idx, old_idx, diff)
{
diff = router_db_diff_get(
&new_router,
&old_router,
new_idx,
old_idx);
if (diff_case_upd(diff))
{
if (!ucentral_router_fib_info_cmp(
&router_db_get(&old_router, old_idx)->info,
&router_db_get(&new_router, new_idx)->info))
continue;
const auto &node = *router_db_get(&new_router, new_idx);
remove_route(0, node.key);
create_route(0, node.key, node.info);
}
if (diff_case_del(diff))
{
remove_route(
0,
router_db_get(&old_router, old_idx)->key);
}
if (diff_case_add(diff))
{
const auto &node = *router_db_get(&new_router, new_idx);
create_route(0, node.key, node.info);
}
}
ucentral_router_fib_db_free(&old_router);
state->router = new_router;
}
} // namespace larch

View File

@@ -0,0 +1,29 @@
#ifndef LARCH_PLATFORM_ROUTE_HPP_
#define LARCH_PLATFORM_ROUTE_HPP_
#include <router-utils.h>
#include <ucentral-platform.h>
#include <cstdint>
#include <vector>
namespace larch {
void create_route(
std::uint16_t router_id,
const ucentral_router_fib_key &fib_key,
const ucentral_router_fib_info &fib_info);
void remove_route(
std::uint16_t router_id,
const ucentral_router_fib_key &fib_key);
std::vector<ucentral_router_fib_node> get_routes(std::uint16_t router_id);
std::vector<plat_gw_address> get_gw_addresses();
void apply_route_config(plat_cfg *cfg);
} // namespace larch
#endif // !LARCH_PLATFORM_ROUTE_HPP_

View File

@@ -0,0 +1,139 @@
#include <sai_redis.hpp>
#include <state.hpp>
#include <sw/redis++/redis++.h>
#include <cstddef>
#include <cstdint>
#include <iterator> // std::inserter
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace larch::sai {
std::unordered_map<object_id, object_id> get_bridge_port_mapping()
{
std::unordered_map<object_id, object_id> mapping;
std::int64_t cursor = 0;
std::unordered_set<std::string> keys;
do
{
constexpr std::string_view pattern =
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:*";
// Example key is
// ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000616
// and we need to get only the 3a00... part. -1 here is for the
// trailing '*' at the end of the pattern.
constexpr std::size_t offset =
pattern.size() - 1 + oid_prefix.size();
keys.clear();
cursor = state->redis_asic->scan(
cursor,
pattern,
std::inserter(keys, keys.begin()));
for (const auto &key : keys)
{
std::unordered_map<std::string, std::string> entry;
state->redis_asic->hgetall(
key,
std::inserter(entry, entry.begin()));
const auto it =
entry.find("SAI_BRIDGE_PORT_ATTR_PORT_ID");
if (it != entry.cend())
{
mapping[key.substr(offset)] =
it->second.substr(oid_prefix.size());
}
}
} while (cursor != 0);
return mapping;
}
std::unordered_map<object_id, std::string> get_port_name_mapping()
{
std::unordered_map<std::string, std::string> entry;
state->redis_counters->hgetall(
"COUNTERS_PORT_NAME_MAP",
std::inserter(entry, entry.begin()));
state->redis_counters->hgetall(
"COUNTERS_LAG_NAME_MAP",
std::inserter(entry, entry.begin()));
std::unordered_map<object_id, std::string> mapping;
for (auto it = entry.cbegin(); it != entry.cend();)
{
// TO-DO: validate interface name?
auto node = entry.extract(it++);
mapping.try_emplace(
node.mapped().substr(oid_prefix.size()),
std::move(node.key()));
}
return mapping;
}
std::optional<std::uint16_t> get_vlan_by_oid(const object_id &oid)
{
std::int64_t cursor = 0;
std::unordered_set<std::string> keys;
const std::string pattern =
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:" + std::string{oid_prefix} + oid;
// There is no guarantee that the necessary key will be found during the
// first scan, so we need to scan until we find it
do
{
keys.clear();
cursor = state->redis_asic->scan(
cursor,
pattern,
std::inserter(keys, keys.begin()));
if (keys.empty())
continue;
std::unordered_map<std::string, std::string> entry;
state->redis_asic->hgetall(
*keys.begin(),
std::inserter(entry, entry.begin()));
const auto it = entry.find("SAI_VLAN_ATTR_VLAN_ID");
if (it == entry.cend())
return std::nullopt;
try
{
return static_cast<std::uint16_t>(
std::stoul(it->second));
}
catch (const std::logic_error &)
{
throw std::runtime_error{"Failed to parse VLAN ID"};
}
{}
} while (cursor != 0);
throw std::runtime_error{"Failed to get VLAN by object ID"};
}
} // namespace larch::sai

View File

@@ -0,0 +1,46 @@
/**
* @file sai_redis.hpp
* @brief Commonly used functions to interact with SAI via Redis DB.
*
* Note, that object IDs (OIDs) are used without "oid:0x" prefix.
*/
#ifndef LARCH_PLATFORM_SAI_REDIS_HPP_
#define LARCH_PLATFORM_SAI_REDIS_HPP_
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
namespace larch::sai {
using object_id = std::string;
inline constexpr std::string_view oid_prefix = "oid:0x";
/**
* @brief Get mapping of bridge port OIDs to port OIDs.
*
* @return Map with bridge port OID as a key and port OID as a value.
*/
std::unordered_map<object_id, object_id> get_bridge_port_mapping();
/**
* @brief Get mapping of port OIDs to port names.
*
* @return Map with port OID and port name as a value.
*/
std::unordered_map<object_id, std::string> get_port_name_mapping();
/**
* @brief Get VLAN ID by its OID.
*
* @throw std::runtime_error If VLAN can't be found
*/
std::optional<std::uint16_t> get_vlan_by_oid(const object_id &oid);
} // namespace larch::sai
#endif // !LARCH_PLATFORM_SAI_REDIS_HPP_

View File

@@ -0,0 +1,114 @@
#include <services.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <arpa/inet.h>
#include <array>
#include <string>
#include <unordered_set>
using nlohmann::json;
namespace larch {
static std::unordered_set<std::string> get_existing_ntp_servers()
{
const json existing_servers_json = json::parse(
gnmi_get("/sonic-ntp:sonic-ntp/NTP_SERVER/NTP_SERVER_LIST"));
std::unordered_set<std::string> existing_servers;
if (existing_servers_json.contains("sonic-ntp:NTP_SERVER_LIST"))
{
for (const auto &server_json :
existing_servers_json.at("sonic-ntp:NTP_SERVER_LIST"))
{
if (!server_json.contains("server_address"))
continue;
existing_servers.insert(
server_json.at("server_address")
.template get<std::string>());
}
}
return existing_servers;
}
static void apply_ntp_config(const plat_ntp_cfg *ntp_cfg)
{
if (ntp_cfg->servers)
{
gnmi_operation op;
std::unordered_set<std::string> existing_servers =
get_existing_ntp_servers();
json ntp_json;
json &server_list_json = ntp_json["sonic-ntp:NTP_SERVER_LIST"];
server_list_json = json::array();
const plat_ntp_server *it = nullptr;
UCENTRAL_LIST_FOR_EACH_MEMBER(it, &ntp_cfg->servers)
{
const auto existing_it =
existing_servers.find(it->hostname);
if (existing_it != existing_servers.cend())
{
existing_servers.erase(existing_it);
continue;
}
std::array<unsigned char, sizeof(in6_addr)> addr_buf{};
if (inet_pton(AF_INET, it->hostname, addr_buf.data())
!= 1
&& inet_pton(
AF_INET6,
it->hostname,
addr_buf.data())
!= 1)
{
UC_LOG_ERR(
"Domains are not supported in NTP server "
"list, use IP addresses");
continue;
}
json server_json;
server_json["association_type"] = "server";
server_json["server_address"] = it->hostname;
server_json["resolve_as"] = it->hostname;
server_list_json.push_back(server_json);
}
op.add_update(
"/sonic-ntp:sonic-ntp/NTP_SERVER/NTP_SERVER_LIST",
ntp_json.dump());
for (const auto &server : existing_servers)
{
op.add_delete(
"/sonic-ntp:sonic-ntp/NTP_SERVER/"
"NTP_SERVER_LIST[server_address="
+ server + "]");
}
op.execute();
}
}
void apply_services_config(plat_cfg *cfg)
{
apply_ntp_config(&cfg->ntp_cfg);
}
} // namespace larch

View File

@@ -0,0 +1,12 @@
#ifndef LARCH_PLATFORM_SERVICES_HPP_
#define LARCH_PLATFORM_SERVICES_HPP_
#include <ucentral-platform.h>
namespace larch {
void apply_services_config(plat_cfg *cfg);
}
#endif // !LARCH_PLATFORM_SERVICES_HPP_

View File

@@ -0,0 +1,10 @@
#include <metrics.hpp>
#include <state.hpp>
#include <sw/redis++/redis++.h>
namespace larch {
platform_state::~platform_state() = default;
} // namespace larch

View File

@@ -0,0 +1,53 @@
#ifndef LARCH_PLATFORM_STATE_HPP_
#define LARCH_PLATFORM_STATE_HPP_
#include <gnmi.grpc.pb.h>
#include <gnmi.pb.h>
#include <sonic_gnoi.grpc.pb.h>
#include <sonic_gnoi.pb.h>
#include <system.grpc.pb.h>
#include <system.pb.h>
#include <grpc++/grpc++.h>
#include <router-utils.h>
#include <ucentral-platform.h>
#include <memory>
#include <vector>
// Forward declarations
namespace sw::redis {
class Redis;
}
namespace larch {
class periodic;
struct platform_state {
~platform_state();
std::shared_ptr<grpc::ChannelInterface> channel;
std::unique_ptr<gnmi::gNMI::Stub> gnmi_stub;
std::unique_ptr<gnoi::system::System::Stub> system_gnoi_stub;
std::unique_ptr<gnoi::sonic::SonicService::Stub> stub_gnoi_sonic;
std::unique_ptr<periodic> telemetry_periodic;
std::unique_ptr<periodic> state_periodic;
std::unique_ptr<periodic> health_periodic;
std::unique_ptr<sw::redis::Redis> redis_asic;
std::unique_ptr<sw::redis::Redis> redis_counters;
std::vector<plat_ipv4> interfaces_addrs;
ucentral_router router{};
};
inline std::unique_ptr<platform_state> state;
} // namespace larch
#endif // !LARCH_PLATFORM_STATE_HPP_

View File

@@ -0,0 +1,107 @@
#include <stp.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <string>
using nlohmann::json;
namespace larch {
void apply_stp_config(struct plat_cfg *cfg)
{
std::size_t i = 0;
gnmi_operation op;
switch (cfg->stp_mode) {
case PLAT_STP_MODE_NONE:
{
const auto stp_list = gnmi_get("/sonic-spanning-tree:sonic-spanning-tree/STP/STP_LIST");
const json stp_list_json = json::parse(stp_list);
/* There are no STPs */
if (!stp_list_json.contains("sonic-spanning-tree:STP_LIST"))
return;
/* This will clear all per port/vlan stp entries */
/* Delete global config since you cannot change the stp mode otherwise */
op.add_delete("/sonic-spanning-tree:sonic-spanning-tree/STP/STP_LIST[keyleaf=GLOBAL]");
break;
}
case PLAT_STP_MODE_PVST:
{
/* Config mode */
json stp_cfg_mode_json;
stp_cfg_mode_json["priority"] = cfg->stp_instances[0].priority;
stp_cfg_mode_json["keyleaf"] = "GLOBAL";
stp_cfg_mode_json["bpdu_filter"] = false;
stp_cfg_mode_json["mode"] = "pvst";
stp_cfg_mode_json["rootguard_timeout"] = 30;
json add_stp_cfg_mode_json;
add_stp_cfg_mode_json["sonic-spanning-tree:STP_LIST"] = {stp_cfg_mode_json};
op.add_update("/sonic-spanning-tree:sonic-spanning-tree/STP/STP_LIST", add_stp_cfg_mode_json.dump());
/* Once mode enabled - create entries for all ports */
BITMAP_FOR_EACH_BIT_SET(i, cfg->ports_to_cfg, MAX_NUM_OF_PORTS)
{
json stp_port_json;
stp_port_json["ifname"] = "Ethernet" + std::to_string(i);
stp_port_json["enabled"] = true;
json add_stp_port_json;
add_stp_port_json["sonic-spanning-tree:STP_PORT_LIST"] = {stp_port_json};
op.add_update("/sonic-spanning-tree:sonic-spanning-tree/STP_PORT/STP_PORT_LIST", add_stp_port_json.dump());
}
/* Config vlans */
for (i = FIRST_VLAN; i < MAX_VLANS; i++) {
if (!cfg->stp_instances[i].enabled) {
continue;
}
json stp_vlan_json;
stp_vlan_json["vlanid"] = i;
stp_vlan_json["name"] = "Vlan" + std::to_string(i);
stp_vlan_json["enabled"] = cfg->stp_instances[i].enabled;
stp_vlan_json["priority"] = cfg->stp_instances[i].priority;
stp_vlan_json["forward_delay"] = cfg->stp_instances[i].forward_delay;
stp_vlan_json["hello_time"] = cfg->stp_instances[i].hello_time;
stp_vlan_json["max_age"] = cfg->stp_instances[i].max_age;
json add_stp_vlan_json;
add_stp_vlan_json["sonic-spanning-tree:STP_VLAN_LIST"] = {stp_vlan_json};
UC_LOG_DBG(
"set vlan=%d state.enabled=%d state.priority=%d "
"state.forward_delay=%d state.hello_time=%d "
"state.max_age=%d ",
i,
cfg->stp_instances[i].enabled,
cfg->stp_instances[i].priority,
cfg->stp_instances[i].forward_delay,
cfg->stp_instances[i].hello_time,
cfg->stp_instances[i].max_age);
op.add_update("/sonic-spanning-tree:sonic-spanning-tree/STP_VLAN/STP_VLAN_LIST", add_stp_vlan_json.dump());
}
break;
}
default:
throw std::runtime_error{"Failed apply stp config"};
}
op.execute();
}
}

View File

@@ -0,0 +1,12 @@
#ifndef LARCH_PLATFORM_STP_HPP_
#define LARCH_PLATFORM_STP_HPP_
#include <ucentral-platform.h>
namespace larch {
void apply_stp_config(plat_cfg *cfg);
}
#endif // !LARCH_PLATFORM_STP_HPP_

View File

@@ -0,0 +1,61 @@
#include <syslog.hpp>
#include <utils.hpp>
#include <nlohmann/json.hpp>
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
#include <ucentral-log.h>
#include <ucentral-platform.h>
#include <string>
using nlohmann::json;
namespace larch {
void gnma_syslog_cfg_clear(void)
{
std::string path = "/sonic-syslog:sonic-syslog/SYSLOG_SERVER/SYSLOG_SERVER_LIST";
gnmi_operation op;
op.add_delete(path);
op.execute();
}
void apply_syslog_config(struct plat_syslog_cfg *cfg, int count)
{
std::string path = "/sonic-syslog:sonic-syslog/SYSLOG_SERVER/SYSLOG_SERVER_LIST";
const std::array<std::string, 8> priority2str = {
"crit",
"crit",
"crit",
"error",
"warn",
"notice",
"info",
"debug"
};
int i;
gnmi_operation op;
for (i = 0; i < count; ++i)
{
json syslog_cfg_member_json;
syslog_cfg_member_json["server_address"] = cfg[i].host;
syslog_cfg_member_json["port"] = cfg[i].port;
syslog_cfg_member_json["protocol"] = cfg[i].is_tcp ? "tcp" : "udp";
syslog_cfg_member_json["severity"] = priority2str.at(cfg[i].priority);
syslog_cfg_member_json["vrf"] = "default";
json add_syslog_cfg_member_json;
add_syslog_cfg_member_json["sonic-syslog:SYSLOG_SERVER_LIST"] = {syslog_cfg_member_json};
op.add_update(path, add_syslog_cfg_member_json.dump());
}
gnma_syslog_cfg_clear();
op.execute();
}
}

View File

@@ -0,0 +1,14 @@
#ifndef LARCH_PLATFORM_SYSLOG_HPP_
#define LARCH_PLATFORM_SYSLOG_HPP_
#include <ucentral-platform.h>
namespace larch {
void gnma_syslog_cfg_clear(void);
void apply_syslog_config(struct plat_syslog_cfg *cfg, int count);
}
#endif // !LARCH_PLATFORM_SYSLOG_HPP_

View File

@@ -0,0 +1,308 @@
#include <state.hpp>
#include <utils.hpp>
#include <gnmi.grpc.pb.h>
#include <gnmi.pb.h>
#include <grpcpp/grpcpp.h>
#include <arpa/inet.h>
#include <array>
#include <cstring>
#include <map>
#include <stdexcept>
#include <string>
#include <utility> // std::move
#include <vector>
namespace larch {
std::vector<std::string>
split_string(std::string str, const std::string &delimiter)
{
std::vector<std::string> results;
std::string::size_type pos{};
while ((pos = str.find(delimiter)) != std::string::npos)
{
results.push_back(str.substr(0, pos));
str.erase(0, pos + 1);
}
// Process the last part
results.push_back(std::move(str));
return results;
}
std::map<std::string, std::string> parse_kv(const std::string &kv_str)
{
enum class parse_state { open_bracket, close_bracket, key, value };
parse_state state = parse_state::close_bracket;
std::map<std::string, std::string> kv;
std::string key_buf, value_buf;
for (const char c : kv_str)
{
switch (c)
{
case '[':
if (state != parse_state::close_bracket)
throw std::runtime_error{
"Unexpected opening bracket"};
state = parse_state::open_bracket;
break;
case ']':
if (state != parse_state::value)
throw std::runtime_error{
"Unexpected closing bracket"};
state = parse_state::close_bracket;
if (key_buf.empty())
throw std::runtime_error{"Empty key"};
if (value_buf.empty())
throw std::runtime_error{"Empty value"};
kv.emplace(
std::move(key_buf),
std::move(value_buf));
key_buf.clear();
value_buf.clear();
break;
case '=':
if (state != parse_state::key)
throw std::runtime_error{
"Unexpected equals sign"};
state = parse_state::value;
break;
default:
{
if (state == parse_state::open_bracket)
state = parse_state::key;
if (state == parse_state::key)
key_buf.push_back(c);
else if (state == parse_state::value)
value_buf.push_back(c);
else
throw std::runtime_error{
"Unexpected character '"
+ std::string{c} + "'"};
break;
}
}
}
if (state != parse_state::close_bracket)
throw std::runtime_error{"Couldn't find closing bracket"};
return kv;
}
void convert_yang_path_to_proto(std::string yang_path, gnmi::Path *proto_path)
{
struct path_element {
std::string name;
std::map<std::string, std::string> kv;
};
std::vector<path_element> elements;
auto process_elem_str = [&elements](std::string elem_str) {
if (!elem_str.empty())
{
const auto open_bracket_pos = elem_str.find('[');
if (open_bracket_pos == std::string::npos)
{
elements.push_back({std::move(elem_str), {}});
}
else
{
// Parse the key-value part of YANG path
// (e.g. [Vlan=100][SomeKey=SomeValue]...)
try
{
elements.push_back(
{elem_str.substr(
0,
open_bracket_pos),
parse_kv(elem_str.substr(
open_bracket_pos))});
}
catch (const std::runtime_error &ex)
{
using namespace std::string_literals;
throw std::runtime_error{
"Failed to parse key-value part of YANG path: "s
+ ex.what()};
}
}
}
};
std::string::size_type pos{};
while ((pos = yang_path.find('/')) != std::string::npos)
{
process_elem_str(yang_path.substr(0, pos));
yang_path.erase(0, pos + 1);
}
// Process the last part of split string
process_elem_str(std::move(yang_path));
std::string &first_element = elements[0].name;
const auto colon_pos = first_element.find(':');
if (colon_pos != std::string::npos)
{
proto_path->set_origin(first_element.substr(0, colon_pos));
first_element.erase(0, colon_pos + 1);
}
for (const auto &elem : elements)
{
gnmi::PathElem *path_elem = proto_path->add_elem();
path_elem->set_name(elem.name);
if (!elem.kv.empty())
{
auto path_kv = path_elem->mutable_key();
for (const auto &[key, value] : elem.kv)
(*path_kv)[key] = value;
}
}
}
std::string gnmi_get(const std::string &yang_path)
{
gnmi::GetRequest greq;
greq.set_encoding(gnmi::JSON_IETF);
convert_yang_path_to_proto(yang_path, greq.add_path());
grpc::ClientContext context;
gnmi::GetResponse gres;
const grpc::Status status = state->gnmi_stub->Get(&context, greq, &gres);
if (!status.ok())
{
throw gnmi_exception{
"gNMI get operation wasn't successful: "
+ status.error_message() + "; error code "
+ std::to_string(status.error_code())};
}
if (gres.notification_size() != 1)
{
throw gnmi_exception{"Unsupported notification size"};
}
gnmi::Notification notification = gres.notification(0);
if (notification.update_size() != 1)
{
throw gnmi_exception{"Unsupported update size"};
}
gnmi::Update update = notification.update(0);
if (!update.has_val())
{
throw gnmi_exception{"Empty value"};
}
gnmi::TypedValue value = update.val();
if (!value.has_json_ietf_val())
{
throw gnmi_exception{"Empty JSON value"};
}
return value.json_ietf_val();
}
void gnmi_set(const std::string &yang_path, const std::string &json_data)
{
gnmi_operation op;
op.add_update(yang_path, json_data);
op.execute();
}
void gnmi_operation::add_update(const std::string &yang_path, const std::string &json_data)
{
gnmi::Update *update = set_request_.add_update();
convert_yang_path_to_proto(yang_path, update->mutable_path());
update->mutable_val()->set_json_ietf_val(json_data);
}
void gnmi_operation::add_delete(const std::string &yang_path)
{
convert_yang_path_to_proto(yang_path, set_request_.add_delete_());
}
void gnmi_operation::execute()
{
grpc::ClientContext context;
gnmi::SetResponse response;
const grpc::Status status = state->gnmi_stub->Set(&context, set_request_, &response);
set_request_.Clear();
if (!status.ok())
{
throw gnmi_exception{
"gNMI set operation wasn't successful: "
+ status.error_message() + "; error code "
+ std::to_string(status.error_code())};
}
}
std::string addr_to_str(const in_addr &address)
{
std::array<char, INET_ADDRSTRLEN> addr_str_buf{};
if (!inet_ntop(
AF_INET,
&address,
addr_str_buf.data(),
addr_str_buf.size()))
{
throw std::runtime_error{
"Failed to convert binary IP address to string"};
}
return addr_str_buf.data();
}
cidr parse_cidr(const std::string &str)
{
cidr result{};
std::array<char, 16> addr_buf{};
if (std::sscanf(
str.c_str(),
"%15[^/]/%hhu",
addr_buf.data(),
&result.mask)
!= 2)
throw std::runtime_error{"Failed to parse CIDR notation"};
result.ip_address = addr_buf.data();
return result;
}
}

Some files were not shown because too many files have changed in this diff Show More