From dfde0d5ece42876a44cb3c46714bc066a4379e98 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Wed, 26 Mar 2025 08:49:12 +0900 Subject: [PATCH 1/7] Adding a enale/disable support for gcov and address-sanitizer A gcov and addresssanitizer are generic source code quality improvement tools. It's a good tool for testing. This patch add enale/disable support for gcov and address-sanitizer. --- .github/workflows/compile.yml | 1 + configure.ac | 25 +++++++++++++++++++++++++ src/GNUmakefile.am | 2 ++ 3 files changed, 28 insertions(+) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index c78bb9d..9183d99 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -41,6 +41,7 @@ jobs: intltool \ libtool \ make \ + pkg-config \ sed - name: autogen diff --git a/configure.ac b/configure.ac index 944d4bd..5c7f63a 100644 --- a/configure.ac +++ b/configure.ac @@ -115,6 +115,31 @@ if test "${CONFIG_ERROR_LOG}" = "no"; then AC_DEFINE(DISABLE_ERROR_LOG, 1, [disable error logging]) fi +# +# Enable gcov +# +AC_MSG_CHECKING([whether to enable gcov]) +AC_ARG_ENABLE([gcov], + [AS_HELP_STRING([--enable-gcov], [Enable gcov in build time (for debug, default is no)])], + [:], + [enable_gcov=no]) +AC_MSG_RESULT([$enable_gcov]) +if test "$enable_gcov" = "yes"; then + CFLAGS="${CFLAGS} -coverage" +fi + +# +# Enable address-sanitizer +# +AC_MSG_CHECKING([whether to enable address-sanitizer]) +AC_ARG_ENABLE([address-sanitizer], + [AS_HELP_STRING([--enable-address-sanitizer], [Enable address sanitizer in build time (for debug, default is no)])], + [:], + [enable_address_sanitizer=no]) +AC_MSG_RESULT([$enable_address_sanitizer]) +if test "$enable_address_sanitizer" = "yes"; then + CFLAGS="${CFLAGS} -fsanitize=address" +fi AC_CONFIG_FILES([ GNUmakefile diff --git a/src/GNUmakefile.am b/src/GNUmakefile.am index 1709bbe..6d0fe0a 100644 --- a/src/GNUmakefile.am +++ b/src/GNUmakefile.am @@ -15,3 +15,5 @@ libsocketcan_la_LDFLAGS = \ # MAINTAINERCLEANFILES = \ GNUmakefile.in + +CLEANFILES = *.gcda *.gcno \ No newline at end of file From c717495e8e7ad9901bb173d1eaadd61f38d91273 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Fri, 28 Mar 2025 02:55:17 +0900 Subject: [PATCH 2/7] Split out common static functions to libsocketcan-utils.c libsocketcan.c has some common function between libcangw.c. But that common function is implemented with static. This patch split out common static function to libsocketcan-utils.c. Each function remove static from function. In addition, it's added __attribute__((__visibility__("hidden"))) to avoid symbol export. --- src/GNUmakefile.am | 4 ++- src/libsocketcan-utils.c | 68 ++++++++++++++++++++++++++++++++++++++++ src/libsocketcan-utils.h | 51 ++++++++++++++++++++++++++++++ src/libsocketcan.c | 57 +-------------------------------- 4 files changed, 123 insertions(+), 57 deletions(-) create mode 100644 src/libsocketcan-utils.c create mode 100644 src/libsocketcan-utils.h diff --git a/src/GNUmakefile.am b/src/GNUmakefile.am index 6d0fe0a..d78b21b 100644 --- a/src/GNUmakefile.am +++ b/src/GNUmakefile.am @@ -4,7 +4,9 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/include \ -I$(top_builddir)/include -libsocketcan_la_SOURCES = libsocketcan.c +libsocketcan_la_SOURCES = \ + libsocketcan-utils.c \ + libsocketcan.c libsocketcan_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/src/libsocketcan-utils.c b/src/libsocketcan-utils.c new file mode 100644 index 0000000..aa83ff6 --- /dev/null +++ b/src/libsocketcan-utils.c @@ -0,0 +1,68 @@ +/* libsocketcan-utils.c + * + * (C) 2009 Luotao Fu + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/** + * @file + * @brief libsocketcan utilities + */ +#include "libsocketcan-utils.h" + +__attribute__((__visibility__("hidden"))) +int addattr32(struct nlmsghdr *n, size_t maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + fprintf(stderr, + "addattr32: Error! max allowed bound %zu exceeded\n", + maxlen); + return -1; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + + return 0; +} + +__attribute__((__visibility__("hidden"))) +int addattr_l(struct nlmsghdr *n, size_t maxlen, int type, + const void *data, int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, + "addattr_l ERROR: message exceeded bound of %zu\n", + maxlen); + return -1; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + + return 0; +} \ No newline at end of file diff --git a/src/libsocketcan-utils.h b/src/libsocketcan-utils.h new file mode 100644 index 0000000..1709b64 --- /dev/null +++ b/src/libsocketcan-utils.h @@ -0,0 +1,51 @@ +/* libsocketcan-utils.h + * + * (C) 2009 Luotao Fu + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef LIBSOCKETCAN_UTILS_H +#define LIBSOCKETCAN_UTILS_H + +#ifdef HAVE_CONFIG_H +#include "libsocketcan_config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Define DISABLE_ERROR_LOG to disable printing of error messages to stderr. */ +#ifdef DISABLE_ERROR_LOG +#define perror(x) while (0) { perror(x); } +#define fprintf(stream, format, args...) while (0) { fprintf(stream, format, ##args); } +#endif + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +int addattr32(struct nlmsghdr *n, size_t maxlen, int type, __u32 data); +int addattr_l(struct nlmsghdr *n, size_t maxlen, int type, const void *data, int alen); +#endif //#ifndef LIBSOCKETCAN_UTILS_H \ No newline at end of file diff --git a/src/libsocketcan.c b/src/libsocketcan.c index 50237ed..f961890 100644 --- a/src/libsocketcan.c +++ b/src/libsocketcan.c @@ -21,10 +21,7 @@ * @file * @brief library code */ - -#ifdef HAVE_CONFIG_H -#include "libsocketcan_config.h" -#endif +#include "libsocketcan-utils.h" #include #include @@ -40,18 +37,9 @@ #include -/* Define DISABLE_ERROR_LOG to disable printing of error messages to stderr. */ -#ifdef DISABLE_ERROR_LOG -#define perror(x) while (0) { perror(x); } -#define fprintf(stream, format, args...) while (0) { fprintf(stream, format, ##args); } -#endif - #define parse_rtattr_nested(tb, max, rta) \ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - #define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) #define IF_UP 1 @@ -108,49 +96,6 @@ parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len) } } -static int addattr32(struct nlmsghdr *n, size_t maxlen, int type, __u32 data) -{ - int len = RTA_LENGTH(4); - struct rtattr *rta; - - if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { - fprintf(stderr, - "addattr32: Error! max allowed bound %zu exceeded\n", - maxlen); - return -1; - } - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - memcpy(RTA_DATA(rta), &data, 4); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; - - return 0; -} - -static int addattr_l(struct nlmsghdr *n, size_t maxlen, int type, - const void *data, int alen) -{ - int len = RTA_LENGTH(alen); - struct rtattr *rta; - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { - fprintf(stderr, - "addattr_l ERROR: message exceeded bound of %zu\n", - maxlen); - return -1; - } - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - memcpy(RTA_DATA(rta), data, alen); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - - return 0; -} - /** * @ingroup intern * @brief send_mod_request - send a linkinfo modification request From 36bacb6c193fc2d1a48d86e07b45c877d02f4bdb Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Fri, 28 Mar 2025 03:06:18 +0900 Subject: [PATCH 3/7] Create add/delete function for CAN gateway configuration This patch create cangw_add_rule and cangw_delete_rule functions. These support to modify CAN gateway configuration one by one. --- include/GNUmakefile.am | 1 + include/libsocketcangw.h | 55 ++++++++++ src/GNUmakefile.am | 3 +- src/libcangw.c | 226 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100755 include/libsocketcangw.h create mode 100644 src/libcangw.c diff --git a/include/GNUmakefile.am b/include/GNUmakefile.am index 12d9b04..c4cb17a 100644 --- a/include/GNUmakefile.am +++ b/include/GNUmakefile.am @@ -1,5 +1,6 @@ nobase_include_HEADERS = \ libsocketcan.h \ + libsocketcangw.h \ can_netlink.h MAINTAINERCLEANFILES = \ diff --git a/include/libsocketcangw.h b/include/libsocketcangw.h new file mode 100755 index 0000000..627a9e0 --- /dev/null +++ b/include/libsocketcangw.h @@ -0,0 +1,55 @@ +/* + * libsocketcangw.h + * + * (C) 2025 Naoto Yamaguchi + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but without + * any warranty; without even the implied warranty of merchantability or fitness + * for a particular purpose. see the gnu lesser general public license for more + * details. + * + * you should have received a copy of the gnu lesser general public license + * along with this library; if not, write to the free software foundation, inc., + * 59 temple place, suite 330, boston, ma 02111-1307 usa + */ + + #ifndef _socketcangw_netlink_h + #define _socketcangw_netlink_h + + /** + * @file + * @brief API overview + */ + #include + + #ifdef __cplusplus + extern "C" { + #endif + +#define SOCKETCAN_GW_RULE_ECHO (0x00000001U) +#define SOCKETCAN_GW_RULE_FILTER (0x00000002U) + +struct s_socketcan_gw_rule { + unsigned int src_ifindex; + unsigned int dst_ifindex; + + unsigned int options; + + unsigned int echo; + struct can_filter filter; + }; +typedef struct s_socketcan_gw_rule socketcan_gw_rule_t; + + +int cangw_add_rule(socketcan_gw_rule_t *rule); +int cangw_delete_rule(socketcan_gw_rule_t *rule); + + #ifdef __cplusplus + } + #endif + + #endif \ No newline at end of file diff --git a/src/GNUmakefile.am b/src/GNUmakefile.am index d78b21b..f3f8c8b 100644 --- a/src/GNUmakefile.am +++ b/src/GNUmakefile.am @@ -6,7 +6,8 @@ AM_CPPFLAGS = \ libsocketcan_la_SOURCES = \ libsocketcan-utils.c \ - libsocketcan.c + libsocketcan.c \ + libcangw.c libsocketcan_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/src/libcangw.c b/src/libcangw.c new file mode 100644 index 0000000..04af464 --- /dev/null +++ b/src/libcangw.c @@ -0,0 +1,226 @@ +/* libcangw.c + * + * (C) 2025 Naoto Yamaguchi + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file + * @brief Socket CAN gateway library + */ + +#include +#include + +#include "libsocketcan-utils.h" + +#include + +struct s_request_data { + struct nlmsghdr nh; + struct rtcanmsg rtcan; + char buf[1500]; +}; + +static int send_cangw_request(struct s_request_data *req) +{ + int result = 0; + int ret = -1; + int sock_fd = -1; + struct nlmsghdr *nlh = NULL; + struct nlmsgerr *rte = NULL; + struct sockaddr_nl nladdr; + unsigned char rxbuf[8192]; + + // Open netlink socket interface + sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock_fd < 0) { + result = -1; + goto do_return; + } + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + ret = sendto(sock_fd, req, req->nh.nlmsg_len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); + if (ret < 0) { + result = -1; + goto do_return; + } + + memset(rxbuf, 0, sizeof(rxbuf)); + ret = recv(sock_fd, &rxbuf, sizeof(rxbuf), 0); + if (ret < 0) { + result = -1; + goto do_return; + } + + nlh = (struct nlmsghdr *)rxbuf; + if (nlh->nlmsg_type != NLMSG_ERROR) { + result = -2; + goto do_return; + } + + rte = (struct nlmsgerr *)NLMSG_DATA(nlh); + if (rte->error < 0) { + result = -3; + } + +do_return: + if (sock_fd >= 0) { + close(sock_fd); + } + + return result; +} +/** + * @ingroup intern + * @brief init_req_data - initialize for req data + * + * @param req pointer to s_request_data structure that is initialized by this function. + * @param flags value of the nlmsg_flags + * @param type value of the nlmsg_type + * + * Set a netlink request data from rule to req. + */ +static void init_req_data(struct s_request_data *req, unsigned short flags, unsigned short type) +{ + // Setup common message + memset(req, 0, sizeof(struct s_request_data)); + + req->nh.nlmsg_flags = flags; + req->nh.nlmsg_type = type; + req->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtcanmsg)); + req->nh.nlmsg_seq = 0; + + req->rtcan.can_family = AF_CAN; + req->rtcan.gwtype = CGW_TYPE_CAN_CAN; + req->rtcan.flags = 0; +} + +/** + * @ingroup intern + * @brief operate_rule_options - operate to options of gw rule + * + * @param req pointer to s_request_data structure that is wrote the options. + * @param rule pointer to source data of the gw configuration rule + * + * Set a netlink request data from rule to req. + */ +static void operate_rule_options(struct s_request_data *req, socketcan_gw_rule_t *rule) +{ + if ((rule->options & SOCKETCAN_GW_RULE_ECHO) == SOCKETCAN_GW_RULE_ECHO) { + if (rule->echo == 1) { + req->rtcan.flags |= CGW_FLAGS_CAN_ECHO; + } + } + + if ((rule->options & SOCKETCAN_GW_RULE_FILTER) == SOCKETCAN_GW_RULE_FILTER) { + addattr_l(&req->nh, sizeof(struct s_request_data), CGW_FILTER, &rule->filter, sizeof(struct can_filter)); + } +} + +/** + * @ingroup extern + * cangw_add_rule - add routing rule to can gateway + * @param rule rule data of the can gateway. + * + * @return 0 if success + * @return -1 if operation is failed + * @return -2 if linux does not support can gateway + * @return -3 if argument is invalid + */ +int cangw_add_rule(socketcan_gw_rule_t *rule) +{ + int result = 0; + int ret = -1; + struct s_request_data req; + + if (rule == NULL) { + result = -3; + goto do_return; + } + + // Setup common message + init_req_data(&req, (NLM_F_REQUEST | NLM_F_ACK), RTM_NEWROUTE); + + if ((rule->src_ifindex == 0) || (rule->dst_ifindex == 0)) { + // invalid ifindex + result = -3; + goto do_return; + } + addattr_l(&req.nh, sizeof(req), CGW_SRC_IF, &rule->src_ifindex, sizeof(rule->src_ifindex)); + addattr_l(&req.nh, sizeof(req), CGW_DST_IF, &rule->dst_ifindex, sizeof(rule->dst_ifindex)); + + // operate options + operate_rule_options(&req, rule); + + ret = send_cangw_request(&req); + if (ret < 0) { + result = -1; + goto do_return; + } + +do_return: + return result; +} + +/** + * @ingroup extern + * cangw_delete_rule - delete routing rule to can gateway + * @param rule rule data of the can gateway. + * + * @return 0 if success + * @return -1 if operation is failed + * @return -2 if linux does not support can gateway + * @return -3 if argument is invalid + */ +int cangw_delete_rule(socketcan_gw_rule_t *rule) +{ + int result = 0; + int ret = -1; + struct s_request_data req; + + if (rule == NULL) { + result = -3; + goto do_return; + } + + // Setup common message + init_req_data(&req, (NLM_F_REQUEST | NLM_F_ACK), RTM_DELROUTE); + + if ((rule->src_ifindex == 0) || (rule->dst_ifindex == 0)) { + // invalid ifindex + result = -3; + goto do_return; + } + addattr_l(&req.nh, sizeof(req), CGW_SRC_IF, &rule->src_ifindex, sizeof(rule->src_ifindex)); + addattr_l(&req.nh, sizeof(req), CGW_DST_IF, &rule->dst_ifindex, sizeof(rule->dst_ifindex)); + + // operate options + operate_rule_options(&req, rule); + + ret = send_cangw_request(&req); + if (ret < 0) { + result = -1; + goto do_return; + } + +do_return: + return result; +} \ No newline at end of file From 7c73010f843a24528a01b454ab37c0a27bd1e449 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Fri, 28 Mar 2025 03:07:02 +0900 Subject: [PATCH 4/7] Adding a test code for add/delete function This patch add test code to test to cangw_add_rule and cangw_delete_rule. --- .gitignore | 5 +++++ src/GNUmakefile.am | 4 ++++ src/test/gwtest.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100755 src/test/gwtest.c diff --git a/.gitignore b/.gitignore index f5cc778..531d70e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,13 @@ autom4te.cache/ src/.libs/ src/*.l* +src/*.gcda +src/*.gcno tests/.libs/ tests/test +test/gwtest +test/*.gcda +test/*.gcno *~ *.o diff --git a/src/GNUmakefile.am b/src/GNUmakefile.am index f3f8c8b..e5b2c25 100644 --- a/src/GNUmakefile.am +++ b/src/GNUmakefile.am @@ -19,4 +19,8 @@ libsocketcan_la_LDFLAGS = \ MAINTAINERCLEANFILES = \ GNUmakefile.in +bin_PROGRAMS = gwtwst + +gwtwst_SOURCES = test/gwtest.c + CLEANFILES = *.gcda *.gcno \ No newline at end of file diff --git a/src/test/gwtest.c b/src/test/gwtest.c new file mode 100755 index 0000000..ce6cc42 --- /dev/null +++ b/src/test/gwtest.c @@ -0,0 +1,44 @@ +#include "../libsocketcan-utils.c" +#include "../libcangw.c" + + +int cangw_add_rule_test(void) +{ + int ret = -1; + socketcan_gw_rule_t gw_rule; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vxcan0"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 0; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x3C0; + gw_rule.filter.can_mask = 0xff0; + + ret = cangw_add_rule(&gw_rule); + if (ret < 0) { + fprintf(stdout,"cangw_add_rule is failed ret = %d\n",ret); + return -1; + } + + ret = cangw_delete_rule(&gw_rule); + if (ret < 0) { + fprintf(stdout,"cangw_delete_rule is failed ret = %d\n",ret); + return -2; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = -1; + ret = cangw_add_rule_test(); + fprintf(stdout,"ret = %d\n",ret); + + return 0; +} \ No newline at end of file From f47eadd65b1752d7d50b5cfecf4d10213a2a2206 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Sat, 29 Mar 2025 21:29:10 +0900 Subject: [PATCH 5/7] Create clean function for CAN gateway configuration An cangw_clean_rule function is clean up for CAN gateway configuration of all. This patch implement to that clean function. --- include/libsocketcangw.h | 1 + src/libcangw.c | 32 ++++++++++++++++++++++++++++++++ src/test/gwtest.c | 12 ++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/libsocketcangw.h b/include/libsocketcangw.h index 627a9e0..fab048d 100755 --- a/include/libsocketcangw.h +++ b/include/libsocketcangw.h @@ -47,6 +47,7 @@ typedef struct s_socketcan_gw_rule socketcan_gw_rule_t; int cangw_add_rule(socketcan_gw_rule_t *rule); int cangw_delete_rule(socketcan_gw_rule_t *rule); +int cangw_clean_rule(void); #ifdef __cplusplus } diff --git a/src/libcangw.c b/src/libcangw.c index 04af464..10fbc8a 100644 --- a/src/libcangw.c +++ b/src/libcangw.c @@ -221,6 +221,38 @@ int cangw_delete_rule(socketcan_gw_rule_t *rule) goto do_return; } +do_return: + return result; +} + +/** + * @ingroup extern + * cangw_clean_rule - delete all routing rule to can gateway + * + * @return 0 if success + * @return -1 if operation is failed + * @return -2 if linux does not support can gateway + */ +int cangw_clean_rule(void) +{ + int result = 0; + int ret = -1; + unsigned int ifindex = 0; + struct s_request_data req; + + // Setup common message + init_req_data(&req, (NLM_F_REQUEST | NLM_F_ACK), RTM_DELROUTE); + + // If src and dst ifindex set to 0, the all rule are deleted. + addattr_l(&req.nh, sizeof(req), CGW_SRC_IF, &ifindex, sizeof(ifindex)); + addattr_l(&req.nh, sizeof(req), CGW_DST_IF, &ifindex, sizeof(ifindex)); + + ret = send_cangw_request(&req); + if (ret < 0) { + result = -1; + goto do_return; + } + do_return: return result; } \ No newline at end of file diff --git a/src/test/gwtest.c b/src/test/gwtest.c index ce6cc42..be08d0f 100755 --- a/src/test/gwtest.c +++ b/src/test/gwtest.c @@ -31,6 +31,18 @@ int cangw_add_rule_test(void) return -2; } + ret = cangw_add_rule(&gw_rule); + if (ret < 0) { + fprintf(stdout,"cangw_add_rule is failed ret = %d\n",ret); + return -3; + } + + ret = cangw_clean_rule(if_nametoindex("vcan0"), if_nametoindex("vxcan0")); + if (ret < 0) { + fprintf(stdout,"cangw_clean_rule is failed ret = %d\n",ret); + return -4; + } + return 0; } From 884d3cbdd20b3592bfd5b7e22672ae6e0c7b64f2 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Sun, 30 Mar 2025 19:31:55 +0900 Subject: [PATCH 6/7] Create rule listing function for CAN gateway cangw_get_rules use to getting a all routing rule of the CAN gateway infrastructure. That function allocates memory to save runting informations. cangw_release_rules use to free memory that is allocated by cangw_get_rules. This patch impliment to both function. --- include/libsocketcangw.h | 13 +- src/libcangw.c | 307 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 313 insertions(+), 7 deletions(-) diff --git a/include/libsocketcangw.h b/include/libsocketcangw.h index fab048d..f2a9692 100755 --- a/include/libsocketcangw.h +++ b/include/libsocketcangw.h @@ -43,12 +43,21 @@ struct s_socketcan_gw_rule { struct can_filter filter; }; typedef struct s_socketcan_gw_rule socketcan_gw_rule_t; - + +struct s_socketcan_gw_rules { + size_t rule_num; + socketcan_gw_rule_t **rules; + // internal use + size_t array_num; + }; +typedef struct s_socketcan_gw_rules socketcan_gw_rules_t; int cangw_add_rule(socketcan_gw_rule_t *rule); int cangw_delete_rule(socketcan_gw_rule_t *rule); int cangw_clean_rule(void); - +int cangw_get_rules(socketcan_gw_rules_t **gw_rules); +int cangw_release_rules(socketcan_gw_rules_t *gw_rules); + #ifdef __cplusplus } #endif diff --git a/src/libcangw.c b/src/libcangw.c index 10fbc8a..8d956d7 100644 --- a/src/libcangw.c +++ b/src/libcangw.c @@ -35,11 +35,24 @@ struct s_request_data { char buf[1500]; }; -static int send_cangw_request(struct s_request_data *req) +#define RTCAN_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtcanmsg)))) +#define RTCAN_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtcanmsg)) + +/** + * @ingroup intern + * send_cangw_set_request - send request to add gw rule into kernel + * @param req pointer to request data. + * + * @return 0 if success + * @return -1 if operation is failed + * @return -2 if linux does not support can gateway + * @return -3 if returned fail response + */ +static int send_cangw_set_request(struct s_request_data *req) { int result = 0; - int ret = -1; int sock_fd = -1; + ssize_t ret = -1; struct nlmsghdr *nlh = NULL; struct nlmsgerr *rte = NULL; struct sockaddr_nl nladdr; @@ -88,6 +101,187 @@ static int send_cangw_request(struct s_request_data *req) return result; } + +/** + * @ingroup intern + * push_gw_rule - push gw rule into gw_rules + * @param gw_rules pointer to rules structure of the can gateway. + * @param rule pointer to new rule to push into gw_rules. + * + * @return 0 if success + * @return -1 if operation is failed + */ +static int push_gw_rule(socketcan_gw_rules_t *gw_rules, socketcan_gw_rule_t *rule) +{ + int result = 0; + + if (gw_rules->rules == NULL) { + // Create rules array + gw_rules->rule_num = 0; // Initial size + gw_rules->array_num = 2; // Initial size + gw_rules->rules = (socketcan_gw_rule_t**)malloc(sizeof(socketcan_gw_rule_t*) * gw_rules->array_num); + if (gw_rules->rules == NULL) { + result = -1; + goto do_return; + } + } + + if (!(gw_rules->rule_num < gw_rules->array_num)) { + // Extend array + socketcan_gw_rule_t **pnew_rules = NULL; + gw_rules->array_num = gw_rules->array_num * 2; + pnew_rules = (socketcan_gw_rule_t**)realloc(gw_rules->rules, (sizeof(socketcan_gw_rule_t*) * gw_rules->array_num)); + if (pnew_rules != NULL) { + gw_rules->rules = pnew_rules; + } else { + result = -1; + goto do_return; + } + } + + gw_rules->rules[gw_rules->rule_num] = rule; + gw_rules->rule_num = gw_rules->rule_num + 1; + +do_return: + return result; +} + +/** + * @ingroup intern + * free_gw_rules - free memory of gw_rules + * @param gw_rules pointer to rules structure of the can gateway. + * + * @return 0 if success + */ +static int free_gw_rules(socketcan_gw_rules_t *gw_rules) +{ + for(size_t i=0; i < gw_rules->rule_num; i++) { + free(gw_rules->rules[i]); + gw_rules->rules[i] = NULL; + } + + free(gw_rules->rules); + gw_rules->rules = NULL; + gw_rules->rule_num = 0; + gw_rules->array_num = 0; + + return 0; +} + +/** + * @ingroup intern + * parse_listing_data - parse routing data that get from kernel + * @param gw_rules rules pointer to rules structure of the can gateway to add rule element. + * @param rxbuf buffer of received data from kernel. + * @param len buffer length of received data from kernel. + * + * @return 1 if completed to get routing rule from kernel + * @return 0 if end of received data + * @return -1 if operation is failed + */ +static int parse_listing_data(socketcan_gw_rules_t *gw_rules, unsigned char *rxbuf, int len) +{ + socketcan_gw_rule_t *rule = NULL; + struct rtcanmsg *rtc = NULL; + struct rtattr *rta = NULL; + struct nlmsghdr *nlh = NULL; + int rtlen = 0; + int result = 0; + + nlh = (struct nlmsghdr*)rxbuf; + + while (1) { + if (!NLMSG_OK(nlh, len)){ + result = 0; + break; + } + + if (nlh->nlmsg_type == NLMSG_ERROR) { + result = -1; + break; + } + + if (nlh->nlmsg_type == NLMSG_DONE) { + result = 1; + break; + } + + rtc = (struct rtcanmsg *)NLMSG_DATA(nlh); + if (rtc->can_family != AF_CAN) { + result = -1; + break; + } + + if (rtc->gwtype != CGW_TYPE_CAN_CAN) { + result = -1; + break; + } + + rule = (socketcan_gw_rule_t*)malloc(sizeof(socketcan_gw_rule_t)); + if (rule == NULL) { + result = -1; + goto error_return; + } + memset(rule, 0 ,sizeof(socketcan_gw_rule_t)); + + rta = (struct rtattr *) RTCAN_RTA(rtc); + rtlen = RTCAN_PAYLOAD(nlh); + while (RTA_OK(rta, rtlen)) { + switch(rta->rta_type) { + case CGW_SRC_IF: + rule->src_ifindex = (*(unsigned int*)RTA_DATA(rta)); + break; + case CGW_DST_IF: + rule->dst_ifindex = (*(unsigned int*)RTA_DATA(rta)); + break; + default: + break; + } + rta = RTA_NEXT(rta, rtlen); + } + + rule->options = (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER); + + if ((rtc->flags & CGW_FLAGS_CAN_ECHO) == CGW_FLAGS_CAN_ECHO) { + rule->echo = 1; + } else { + rule->echo = 0; + } + + rta = (struct rtattr *) RTCAN_RTA(rtc); + rtlen = RTCAN_PAYLOAD(nlh); + while(RTA_OK(rta, rtlen)) { + switch(rta->rta_type) { + case CGW_FILTER: + { + struct can_filter *filter = (struct can_filter *)RTA_DATA(rta); + if (filter->can_id & CAN_INV_FILTER) { + rule->filter.can_id = (filter->can_id & ~CAN_INV_FILTER); + } else { + rule->filter.can_id = filter->can_id; + } + rule->filter.can_mask = filter->can_mask; + break; + } + + default: + break; + } + rta = RTA_NEXT(rta, rtlen); + } + + push_gw_rule(gw_rules, rule); + + nlh = NLMSG_NEXT(nlh, len); + } + + return result; + +error_return: + (void) free(rule); + return result; +} + /** * @ingroup intern * @brief init_req_data - initialize for req data @@ -170,7 +364,7 @@ int cangw_add_rule(socketcan_gw_rule_t *rule) // operate options operate_rule_options(&req, rule); - ret = send_cangw_request(&req); + ret = send_cangw_set_request(&req); if (ret < 0) { result = -1; goto do_return; @@ -215,7 +409,7 @@ int cangw_delete_rule(socketcan_gw_rule_t *rule) // operate options operate_rule_options(&req, rule); - ret = send_cangw_request(&req); + ret = send_cangw_set_request(&req); if (ret < 0) { result = -1; goto do_return; @@ -247,12 +441,115 @@ int cangw_clean_rule(void) addattr_l(&req.nh, sizeof(req), CGW_SRC_IF, &ifindex, sizeof(ifindex)); addattr_l(&req.nh, sizeof(req), CGW_DST_IF, &ifindex, sizeof(ifindex)); - ret = send_cangw_request(&req); + ret = send_cangw_set_request(&req); + if (ret < 0) { + result = -1; + goto do_return; + } + +do_return: + return result; +} + +/** + * @ingroup extern + * cangw_get_rules - get can gateway routing rule + * @param gw_rules double pointer to rules structure of the can gateway to get existing rules. + * + * @return 0 if success + * @return -1 if operation is failed + * @return -2 if linux does not support can gateway + * @return -3 if argument is invalid + */ +int cangw_get_rules(socketcan_gw_rules_t **gw_rules) +{ + int result = 0; + int sock_fd = -1; + ssize_t ret = -1; + struct s_request_data req; + struct sockaddr_nl nladdr; + socketcan_gw_rules_t *pgw_rules = NULL; + + if (gw_rules == NULL) { + result = -3; + goto do_return; + } + + // Setup common message + init_req_data(&req, (NLM_F_REQUEST | NLM_F_DUMP), RTM_GETROUTE); + + // Open netlink socket interface + sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock_fd < 0) { + result = -2; + goto do_return; + } + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + ret = sendto(sock_fd, &req, req.nh.nlmsg_len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); if (ret < 0) { result = -1; goto do_return; } + pgw_rules = malloc(sizeof(socketcan_gw_rules_t)); + if (pgw_rules == NULL) { + result = -1; + goto do_return; + } + + pgw_rules->rule_num = 0; + pgw_rules->array_num = 0; + pgw_rules->rules = NULL; + + while (1) { + unsigned char rxbuf[8192]; + memset(rxbuf, 0, sizeof(rxbuf)); + + ret = recv(sock_fd, &rxbuf, sizeof(rxbuf), 0); + if (ret < 0) { + result = -1; + goto do_return; + } + + /* leave on errors or NLMSG_DONE */ + if (parse_listing_data(pgw_rules, rxbuf, ret)) + break; + } + print_gw_rules(pgw_rules); + (*gw_rules) = pgw_rules; + +do_return: + if (sock_fd >= 0) { + close(sock_fd); + } + + return result; +} +/** + * @ingroup extern + * cangw_release_rules - delete routing rule to can gateway + * @param gw_rules rules pointer to rules structure of the can gateway to free allocated memory. + * + * @return 0 if success + * @return -3 if argument is invalid + */ +int cangw_release_rules(socketcan_gw_rules_t *gw_rules) +{ + int result = 0; + + if (gw_rules == NULL) { + result = -3; + goto do_return; + } + + (void) free_gw_rules(gw_rules); + (void) free(gw_rules); + do_return: return result; } \ No newline at end of file From f2a471f06ff3c52efe0a7fe1f12cd673c434fc35 Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Wed, 2 Apr 2025 02:56:52 +0900 Subject: [PATCH 7/7] Create unit test for gateway functions This patch add some unit test. It uses CMOCKA library that is unit test framework for C language. In addtion, this patch add some shell scripts that enables interface creation and clean up, clean up in case of test is failed, and create coveradge report. --- GNUmakefile.am | 4 + configure.ac | 15 +- src/GNUmakefile.am | 4 - src/libcangw.c | 2 +- src/test/gwtest.c | 56 ------ test/GNUmakefile.am | 15 ++ test/clean-test.sh | 5 + test/gen-test-report.h | 9 + test/gwtest.c | 436 +++++++++++++++++++++++++++++++++++++++++ test/run-test.sh | 13 ++ 10 files changed, 497 insertions(+), 62 deletions(-) delete mode 100755 src/test/gwtest.c create mode 100644 test/GNUmakefile.am create mode 100755 test/clean-test.sh create mode 100755 test/gen-test-report.h create mode 100755 test/gwtest.c create mode 100755 test/run-test.sh diff --git a/GNUmakefile.am b/GNUmakefile.am index ca4fbda..2d11a02 100644 --- a/GNUmakefile.am +++ b/GNUmakefile.am @@ -3,6 +3,10 @@ SUBDIRS = \ config \ src +if ENABLE_TEST +SUBDIRS += test +endif + EXTRA_DIST = \ autogen.sh \ config/m4/.secret-world-domination-project diff --git a/configure.ac b/configure.ac index 5c7f63a..a6fe69b 100644 --- a/configure.ac +++ b/configure.ac @@ -95,7 +95,7 @@ if test "${CONFIG_DEBUG}" = "yes"; then CFLAGS="${CFLAGS} -Werror -Wsign-compare -Wfloat-equal -Wformat-security -g -O1" AC_DEFINE(DEBUG, 1, [debugging]) else - CFLAGS="${CFLAGS} -O2" + CFLAGS="${CFLAGS} -g -O2" fi @@ -141,12 +141,25 @@ if test "$enable_address_sanitizer" = "yes"; then CFLAGS="${CFLAGS} -fsanitize=address" fi +# +# Enable unit test +# +AC_ARG_ENABLE([test], + [AS_HELP_STRING([--enable-test], [Enable unit test build (cmocka is required, default is no])], + [:], + [enable_test=no]) +AM_CONDITIONAL([ENABLE_TEST], [test "$enable_test" = "yes"]) +AS_CASE( + ["$enable_test"], + [yes], [PKG_CHECK_MODULES([CMOCKA], [cmocka])],[]) + AC_CONFIG_FILES([ GNUmakefile config/libsocketcan.pc config/GNUmakefile include/GNUmakefile src/GNUmakefile + test/GNUmakefile ]) AC_OUTPUT diff --git a/src/GNUmakefile.am b/src/GNUmakefile.am index e5b2c25..f3f8c8b 100644 --- a/src/GNUmakefile.am +++ b/src/GNUmakefile.am @@ -19,8 +19,4 @@ libsocketcan_la_LDFLAGS = \ MAINTAINERCLEANFILES = \ GNUmakefile.in -bin_PROGRAMS = gwtwst - -gwtwst_SOURCES = test/gwtest.c - CLEANFILES = *.gcda *.gcno \ No newline at end of file diff --git a/src/libcangw.c b/src/libcangw.c index 8d956d7..d1f02bb 100644 --- a/src/libcangw.c +++ b/src/libcangw.c @@ -520,7 +520,7 @@ int cangw_get_rules(socketcan_gw_rules_t **gw_rules) if (parse_listing_data(pgw_rules, rxbuf, ret)) break; } - print_gw_rules(pgw_rules); + (*gw_rules) = pgw_rules; do_return: diff --git a/src/test/gwtest.c b/src/test/gwtest.c deleted file mode 100755 index be08d0f..0000000 --- a/src/test/gwtest.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../libsocketcan-utils.c" -#include "../libcangw.c" - - -int cangw_add_rule_test(void) -{ - int ret = -1; - socketcan_gw_rule_t gw_rule; - - memset(&gw_rule, 0, sizeof(gw_rule)); - - gw_rule.src_ifindex = if_nametoindex("vcan0"); - gw_rule.dst_ifindex = if_nametoindex("vxcan0"); - - gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; - gw_rule.echo = 0; - - gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; - gw_rule.filter.can_id = 0x3C0; - gw_rule.filter.can_mask = 0xff0; - - ret = cangw_add_rule(&gw_rule); - if (ret < 0) { - fprintf(stdout,"cangw_add_rule is failed ret = %d\n",ret); - return -1; - } - - ret = cangw_delete_rule(&gw_rule); - if (ret < 0) { - fprintf(stdout,"cangw_delete_rule is failed ret = %d\n",ret); - return -2; - } - - ret = cangw_add_rule(&gw_rule); - if (ret < 0) { - fprintf(stdout,"cangw_add_rule is failed ret = %d\n",ret); - return -3; - } - - ret = cangw_clean_rule(if_nametoindex("vcan0"), if_nametoindex("vxcan0")); - if (ret < 0) { - fprintf(stdout,"cangw_clean_rule is failed ret = %d\n",ret); - return -4; - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - int ret = -1; - ret = cangw_add_rule_test(); - fprintf(stdout,"ret = %d\n",ret); - - return 0; -} \ No newline at end of file diff --git a/test/GNUmakefile.am b/test/GNUmakefile.am new file mode 100644 index 0000000..c538d7e --- /dev/null +++ b/test/GNUmakefile.am @@ -0,0 +1,15 @@ +bin_PROGRAMS = gwtest + +gwtest_SOURCES = gwtest.c + +gwtest_LDADD = \ + @CMOCKA_LIBS@ + +gwtest_CFLAGS = \ + -g \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/include \ + @CMOCKA_CFLAGS@ \ + -D_GNU_SOURCE + +CLEANFILES = *.gcda *.gcno \ No newline at end of file diff --git a/test/clean-test.sh b/test/clean-test.sh new file mode 100755 index 0000000..2061358 --- /dev/null +++ b/test/clean-test.sh @@ -0,0 +1,5 @@ +#!/bin/sh +ip link del vxcan0 +ip link del vcan0 +ip link del vcan1 + diff --git a/test/gen-test-report.h b/test/gen-test-report.h new file mode 100755 index 0000000..7786ca2 --- /dev/null +++ b/test/gen-test-report.h @@ -0,0 +1,9 @@ +#!/bin/sh + +rm -Rf ./coverage +mkdir -p ./coverage +mkdir -p ./coverage/report + +lcov -c --rc lcov_branch_coverage=1 -d . -o ./coverage/testcoverage.info +genhtml --rc lcov_branch_coverage=1 -o ./coverage/report -f ./coverage/testcoverage.info --num-spaces 4 + diff --git a/test/gwtest.c b/test/gwtest.c new file mode 100755 index 0000000..f81b5b0 --- /dev/null +++ b/test/gwtest.c @@ -0,0 +1,436 @@ +#include "libsocketcan-utils.c" +#include "libcangw.c" +#include +#include +#include + +int print_gw_rules(socketcan_gw_rules_t *gw_rules) +{ + char src_ifname[IF_NAMESIZE]; + char dst_ifname[IF_NAMESIZE]; + + for(size_t i=0; i < gw_rules->rule_num; i++) { + socketcan_gw_rule_t *rule = gw_rules->rules[i]; + + fprintf(stdout, "cangw: -s %s -d %s -f %03X:%X echo=%d\n", + if_indextoname(rule->src_ifindex, src_ifname), + if_indextoname(rule->dst_ifindex, dst_ifname), + rule->filter.can_id, + rule->filter.can_mask, + rule->echo + ); + } + + return 0; +} +//------------------------------------------------------------------------------------------ +static int rule_setup(void **state) { + int ret = -1; + unsigned long ifindex_tmp = 0; + + ifindex_tmp = if_nametoindex("vcan0"); + if (ifindex_tmp == 0) { + fprintf(stderr,"Did not create vcan0 interface. That interface needs in test.\n"); + return -1; + } + + ifindex_tmp = if_nametoindex("vcan1"); + if (ifindex_tmp == 0) { + fprintf(stderr,"Did not create vcan1 interface. That interface needs in test.\n"); + return -1; + } + + ifindex_tmp = if_nametoindex("vxcan0"); + if (ifindex_tmp == 0) { + fprintf(stderr,"Did not create vxcan0 interface. That interface needs in test.\n"); + return -1; + } + + ret = cangw_clean_rule(); + if (ret < 0) { + fprintf(stderr,"Could not clean rules.\n"); + return -1; + } + + return 0; +} + +static int rule_teardown(void **state) { + int ret = -1; + + ret = cangw_clean_rule(); + if (ret < 0) { + fprintf(stderr,"Could not clean rules.\n"); + return -1; + } + + return 0; +} +//------------------------------------------------------------------------------------------ +static void add_rule_test_10(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vcan1"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 1; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x3C0; + gw_rule.filter.can_mask = 0xff0; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 1); + assert_true(gw_rules->rules[0]->src_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[0]->dst_ifindex == if_nametoindex("vcan1")); + assert_true(gw_rules->rules[0]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[0]->echo == 1); + assert_true(gw_rules->rules[0]->filter.can_id == 0x3C0); + assert_true(gw_rules->rules[0]->filter.can_mask == 0xff0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void add_rule_test_11(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vxcan0"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 0; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x3C0; + gw_rule.filter.can_mask = 0xff0; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 1); + assert_true(gw_rules->rules[0]->src_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[0]->dst_ifindex == if_nametoindex("vxcan0")); + assert_true(gw_rules->rules[0]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[0]->echo == 0); + assert_true(gw_rules->rules[0]->filter.can_id == 0x3C0); + assert_true(gw_rules->rules[0]->filter.can_mask == 0xff0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void add_rule_test_12(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vxcan0"); + gw_rule.dst_ifindex = if_nametoindex("vcan0"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 0; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x188; + gw_rule.filter.can_mask = 0xfff; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 1); + assert_true(gw_rules->rules[0]->src_ifindex == if_nametoindex("vxcan0")); + assert_true(gw_rules->rules[0]->dst_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[0]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[0]->echo == 0); + assert_true(gw_rules->rules[0]->filter.can_id == 0x188); + assert_true(gw_rules->rules[0]->filter.can_mask == 0xfff); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void add_rule_test_13(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule[2]; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule[0], 0, sizeof(gw_rule[0])); + memset(&gw_rule[1], 0, sizeof(gw_rule[1])); + + gw_rule[0].src_ifindex = if_nametoindex("vcan0"); + gw_rule[0].dst_ifindex = if_nametoindex("vcan1"); + gw_rule[0].options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule[0].echo = 1; + gw_rule[0].options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule[0].filter.can_id = 0x001; + gw_rule[0].filter.can_mask = 0x0ff; + + gw_rule[1].src_ifindex = if_nametoindex("vxcan0"); + gw_rule[1].dst_ifindex = if_nametoindex("vcan0"); + gw_rule[1].options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule[1].filter.can_id = 0x00f; + gw_rule[1].filter.can_mask = 0xff0; + + ret = cangw_add_rule(&gw_rule[1]); + assert_true(ret == 0); + + ret = cangw_add_rule(&gw_rule[0]); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 2); + assert_true(gw_rules->rules[0]->src_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[0]->dst_ifindex == if_nametoindex("vcan1")); + assert_true(gw_rules->rules[0]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[0]->echo == 1); + assert_true(gw_rules->rules[0]->filter.can_id == 0x001); + assert_true(gw_rules->rules[0]->filter.can_mask == 0x0ff); + assert_true(gw_rules->rules[1]->src_ifindex == if_nametoindex("vxcan0")); + assert_true(gw_rules->rules[1]->dst_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[1]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[1]->echo == 0); + assert_true(gw_rules->rules[1]->filter.can_id == 0x00f); + assert_true(gw_rules->rules[1]->filter.can_mask == 0xff0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void add_rule_test_14(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vcan1"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 1; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + + canid_t id = 1; + for(int i=0;i < 256;i++) { + gw_rule.filter.can_id = id; + gw_rule.filter.can_mask = 0xfff; + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + id++; + } + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 256); + for(int i=0;i < 256;i++) { + id--; + assert_true(gw_rules->rules[i]->src_ifindex == if_nametoindex("vcan0")); + assert_true(gw_rules->rules[i]->dst_ifindex == if_nametoindex("vcan1")); + assert_true(gw_rules->rules[i]->options == (SOCKETCAN_GW_RULE_ECHO | SOCKETCAN_GW_RULE_FILTER)); + assert_true(gw_rules->rules[i]->echo == 1); + assert_true(gw_rules->rules[i]->filter.can_id == id); + assert_true(gw_rules->rules[i]->filter.can_mask == 0xfff); + } + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void delete_rule_test_20(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vcan1"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 1; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x3C0; + gw_rule.filter.can_mask = 0xff0; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_delete_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void delete_rule_test_21(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vcan0"); + gw_rule.dst_ifindex = if_nametoindex("vxcan0"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 0; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x123; + gw_rule.filter.can_mask = 0x000; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_delete_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static void delete_rule_test_22(void **state) { + int ret = -1; + socketcan_gw_rule_t gw_rule; + socketcan_gw_rules_t *gw_rules = NULL; + + memset(&gw_rule, 0, sizeof(gw_rule)); + + gw_rule.src_ifindex = if_nametoindex("vxcan0"); + gw_rule.dst_ifindex = if_nametoindex("vcan0"); + + gw_rule.options |= SOCKETCAN_GW_RULE_ECHO; + gw_rule.echo = 0; + + gw_rule.options |= SOCKETCAN_GW_RULE_FILTER; + gw_rule.filter.can_id = 0x188; + gw_rule.filter.can_mask = 0xfff; + + ret = cangw_add_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_delete_rule(&gw_rule); + assert_true(ret == 0); + + ret = cangw_get_rules(&gw_rules); + assert_true(ret == 0); + assert_non_null(gw_rules); + assert_true(gw_rules->rule_num == 0); + + cangw_release_rules(gw_rules); +} +//------------------------------------------------------------------------------------------ +static struct option long_options[] = { + {"add-rule", no_argument, 0, 10}, + {"delete-rule", no_argument, 0, 20}, + {"clean-rule", no_argument, 0, 20}, + {0, 0, 0, 0}, +}; + +int main(int argc, char *argv[]) +{ + int ret = -1; + + ret = getopt_long(argc, argv, "", long_options, NULL); + switch (ret) { + case 10: + { + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(add_rule_test_10), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(add_rule_test_11), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(add_rule_test_12), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(add_rule_test_13), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(add_rule_test_14), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + break; + } + case 20: + { + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(delete_rule_test_20), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(delete_rule_test_21), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + { + const struct CMUnitTest tests[] = { + cmocka_unit_test(delete_rule_test_22), + }; + (void) cmocka_run_group_tests(tests, rule_setup, rule_teardown); + } + break; + } + case 30: + { + + break; + } + default: + { + (void) fprintf(stderr, "This option is not support.\n"); + break; + } + } + + return 0; +} \ No newline at end of file diff --git a/test/run-test.sh b/test/run-test.sh new file mode 100755 index 0000000..064b2a6 --- /dev/null +++ b/test/run-test.sh @@ -0,0 +1,13 @@ +#!/bin/sh -e + +ip link add dev vcan0 type vcan +ip link add dev vcan1 type vcan +ip link add vxcan0 type vxcan peer name vxcan1 + +./test/gwtest --add-rule +./test/gwtest --delete-rule + +ip link del vxcan0 +ip link del vcan0 +ip link del vcan1 +