diff --git a/build/ac/Makefile.am b/build/ac/Makefile.am index 3f0e5b0..5206bc3 100755 --- a/build/ac/Makefile.am +++ b/build/ac/Makefile.am @@ -25,15 +25,16 @@ bin_PROGRAMS = ac AM_CFLAGS = \ -DCAPWAP_MULTITHREADING_ENABLE \ -D_REENTRANT \ - -D_GNU_SOURCE - -AM_CFLAGS += $(LIBXML2_CFLAGS) \ + -D_GNU_SOURCE \ + ${LIBNL_CFLAGS} \ + $(LIBXML2_CFLAGS) \ $(CYASSL_CFLAGS) INCLUDES = \ -I$(top_srcdir)/build \ -I$(top_srcdir)/src/common \ -I$(top_srcdir)/src/ac \ + -I$(top_srcdir)/src/ac/kmod \ -I$(top_srcdir)/src/common/binding/ieee80211 include $(top_srcdir)/build/Makefile_common.am @@ -50,6 +51,7 @@ ac_SOURCES = \ $(top_srcdir)/src/ac/ac_session.c \ $(top_srcdir)/src/ac/ac_session_data.c \ $(top_srcdir)/src/ac/ac_wlans.c \ + $(top_srcdir)/src/ac/ac_kmod.c \ $(top_srcdir)/src/ac/ac_ieee80211_data.c \ $(top_srcdir)/src/ac/ac_discovery.c \ $(top_srcdir)/src/ac/ac_80211_json.c \ @@ -90,4 +92,5 @@ ac_LDADD = \ $(PTHREAD_LIBS) \ $(LIBXML2_LIBS) \ $(LIBJSON_LIBS) \ - $(CYASSL_LIBS) + $(CYASSL_LIBS) \ + $(LIBNL_LIBS) diff --git a/build/wtp/Makefile.am b/build/wtp/Makefile.am index 683e5dd..9178a06 100755 --- a/build/wtp/Makefile.am +++ b/build/wtp/Makefile.am @@ -24,7 +24,8 @@ bin_PROGRAMS = wtp AM_CFLAGS = \ -D_REENTRANT \ - -D_GNU_SOURCE + -D_GNU_SOURCE \ + ${LIBNL_CFLAGS} if DTLS_ENABLED AM_CFLAGS += $(CYASSL_CFLAGS) @@ -62,7 +63,8 @@ wtp_SOURCES = \ $(top_srcdir)/src/wtp/binding/ieee80211/wifi_drivers.c wtp_LDADD = \ - $(CONFIG_LIBS) + $(CONFIG_LIBS) \ + $(LIBNL_LIBS) if DTLS_ENABLED wtp_LDADD += $(CYASSL_LIBS) @@ -70,7 +72,4 @@ endif if BUILD_WTP_WIFI_DRIVERS_NL80211 wtp_SOURCES += $(top_srcdir)/src/wtp/binding/ieee80211/wifi_nl80211.c -AM_CFLAGS += ${LIBNL_CFLAGS} -wtp_LDADD += $(LIBNL_LIBS) endif - diff --git a/configure.ac b/configure.ac index 1657814..9ac234c 100644 --- a/configure.ac +++ b/configure.ac @@ -208,22 +208,24 @@ if test "${enable_ac}" = "yes"; then ) fi +# Check Netlink library +PKG_CHECK_MODULES( + [LIBNL], + [libnl-1], + [AC_DEFINE([HAVE_LIBNL_10], [1], [Use libnl-1.0 library])], + [PKG_CHECK_MODULES( + [LIBNL], + [libnl-tiny], + [AC_DEFINE([HAVE_LIBNL_TINY], [1], [Use libnl-tiny library])], + [AC_MSG_ERROR(You need the libnl or libnl-tiny)] + )] +) + +AC_CHECK_HEADERS([netlink/genl/genl.h netlink/genl/family.h netlink/genl/ctrl.h], [], [AC_MSG_ERROR(You need the netlink header)]) +AC_CHECK_HEADER([linux/nl80211.h], [], [AC_MSG_ERROR(You need the nl80211 header)]) + # Check nl80211 if test "${enable_wifi_drivers_nl80211}" = "yes"; then - PKG_CHECK_MODULES( - [LIBNL], - [libnl-1], - [AC_DEFINE([HAVE_LIBNL_10], [1], [Use libnl-1.0 library])], - [PKG_CHECK_MODULES( - [LIBNL], - [libnl-tiny], - [AC_DEFINE([HAVE_LIBNL_TINY], [1], [Use libnl-tiny library])], - [AC_MSG_ERROR(You need the libnl or libnl-tiny)] - )] - ) - - AC_CHECK_HEADERS([netlink/genl/genl.h netlink/genl/family.h netlink/genl/ctrl.h], [], [AC_MSG_ERROR(You need the netlink header)]) - AC_CHECK_HEADER([linux/nl80211.h], [], [AC_MSG_ERROR(You need the nl80211 header)]) AC_DEFINE([ENABLE_WIFI_DRIVERS_NL80211], [1], [Enable WTP support for nl80211 wifi binding]) fi AM_CONDITIONAL([BUILD_WTP_WIFI_DRIVERS_NL80211], [test "${enable_wifi_drivers_nl80211}" = "yes"]) diff --git a/src/ac/ac.c b/src/ac/ac.c index f6c056a..f0cc24d 100644 --- a/src/ac/ac.c +++ b/src/ac/ac.c @@ -833,11 +833,24 @@ int main(int argc, char** argv) { /* Complete configuration AC */ result = ac_configure(); if (result == CAPWAP_SUCCESSFUL) { - /* Running AC */ - result = ac_execute(); + /* Connect AC to kernel module */ + value = ac_kmod_init(); + if (!value || !g_ac.kmodrequest) { + if (ac_kmod_isconnected()) { + capwap_logging_info("SmartCAPWAP kernel module connected"); + } - /* Close connection */ - ac_close(); + /* Running AC */ + result = ac_execute(); + + /* Close connection */ + ac_close(); + + /* Disconnect kernel module */ + ac_kmod_free(); + } else { + capwap_logging_fatal("Unable to connect to kernel module"); + } } } diff --git a/src/ac/ac.h b/src/ac/ac.h index dfa9df5..841e259 100644 --- a/src/ac/ac.h +++ b/src/ac/ac.h @@ -15,6 +15,8 @@ #include #include +#include + /* AC Configuration */ #define AC_DEFAULT_CONFIGURATION_FILE "/etc/capwap/ac.conf" @@ -73,6 +75,21 @@ struct ac_state { struct capwap_acipv6list_element acipv6list; }; +/* */ +struct ac_fds { + int fdstotalcount; + struct pollfd* fdspoll; + + int fdsnetworkcount; + + int msgqueuecount; + int msgqueuestartpos; + + struct ac_kmod_event* kmodevents; + int kmodeventscount; + int kmodeventsstartpos; +}; + /* AC */ struct ac_t { int standalone; @@ -83,6 +100,8 @@ struct ac_t { struct capwap_network net; unsigned short mtu; + struct ac_fds fds; + struct capwap_array* binding; struct capwap_acname_element acname; @@ -91,6 +110,10 @@ struct ac_t { /* Sessions message queue */ int fdmsgsessions[2]; + /* */ + int kmodrequest; + struct ac_kmod_handle kmodhandle; + /* Sessions */ struct capwap_list* sessions; struct capwap_list* sessionsdata; @@ -133,6 +156,7 @@ extern struct ac_t g_ac; /* Primary thread */ int ac_execute(void); +int ac_execute_update_fdspool(struct ac_fds* fds); int ac_valid_binding(unsigned short binding); void ac_update_statistics(void); diff --git a/src/ac/ac_execute.c b/src/ac/ac_execute.c index b01ef09..f94e1a6 100644 --- a/src/ac/ac_execute.c +++ b/src/ac/ac_execute.c @@ -8,6 +8,7 @@ #include #define AC_RECV_NOERROR_MSGQUEUE -1001 +#define AC_RECV_NOERROR_KMODEVENT -1002 /* */ static int ac_recvmsgqueue(int fd, struct ac_session_msgqueue_item_t* item) { @@ -103,11 +104,12 @@ void ac_msgqueue_notify_closethread(pthread_t threadid) { } /* */ -static int ac_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr) { +static int ac_recvfrom(struct ac_fds* fds, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr) { int index; ASSERT(fds); - ASSERT(fdscount > 0); + ASSERT(fds->fdspoll != NULL); + ASSERT(fds->fdstotalcount > 0); ASSERT(buffer != NULL); ASSERT(size != NULL); ASSERT(*size > 0); @@ -115,25 +117,36 @@ static int ac_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size ASSERT(recvtoaddr != NULL); /* Wait packet */ - index = capwap_wait_recvready(fds, fdscount, NULL); + index = capwap_wait_recvready(fds->fdspoll, fds->fdstotalcount, NULL); if (index < 0) { return index; - } else if (index == (fdscount - 1)) { + } else if ((fds->kmodeventsstartpos >= 0) && (index >= fds->kmodeventsstartpos)) { + int pos = index - fds->kmodeventsstartpos; + + if (pos < fds->kmodeventscount) { + if (!fds->kmodevents[pos].event_handler) { + return CAPWAP_RECV_ERROR_SOCKET; + } + + fds->kmodevents[pos].event_handler(fds->fdspoll[index].fd, fds->kmodevents[pos].params, fds->kmodevents[pos].paramscount); + } + + return AC_RECV_NOERROR_KMODEVENT; + } else if ((fds->msgqueuestartpos >= 0) && (index >= fds->msgqueuestartpos)) { struct ac_session_msgqueue_item_t item; /* Receive message queue packet */ - if (!ac_recvmsgqueue(fds[index].fd, &item)) { + if (!ac_recvmsgqueue(fds->fdspoll[index].fd, &item)) { return CAPWAP_RECV_ERROR_SOCKET; } /* Parsing message queue packet */ ac_session_msgqueue_parsing_item(&item); - return AC_RECV_NOERROR_MSGQUEUE; } /* Receive packet */ - if (!capwap_recvfrom_fd(fds[index].fd, buffer, size, recvfromaddr, recvtoaddr)) { + if (!capwap_recvfrom_fd(fds->fdspoll[index].fd, buffer, size, recvfromaddr, recvtoaddr)) { return CAPWAP_RECV_ERROR_SOCKET; } @@ -703,32 +716,118 @@ static void ac_signal_handler(int signum) { } } +/* */ +static int ac_execute_init_fdspool(struct ac_fds* fds, struct capwap_network* net, int fdmsgqueue) { + ASSERT(fds != NULL); + ASSERT(net != NULL); + ASSERT(fdmsgqueue > 0); + + /* */ + memset(fds, 0, sizeof(struct ac_fds)); + fds->fdsnetworkcount = capwap_network_set_pollfd(net, NULL, 0); + fds->msgqueuecount = 1; + fds->fdspoll = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * (fds->fdsnetworkcount + fds->msgqueuecount)); + + /* Retrive all socket for polling */ + fds->fdstotalcount = capwap_network_set_pollfd(net, fds->fdspoll, fds->fdsnetworkcount); + if (fds->fdsnetworkcount != fds->fdstotalcount) { + capwap_free(fds->fdspoll); + return -1; + } + + /* Unix socket message queue */ + fds->msgqueuestartpos = fds->fdsnetworkcount; + fds->fdspoll[fds->msgqueuestartpos].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + fds->fdspoll[fds->msgqueuestartpos].fd = fdmsgqueue; + fds->fdstotalcount += fds->msgqueuecount; + + return ac_execute_update_fdspool(fds); +} + +/* */ +static void ac_execute_free_fdspool(struct ac_fds* fds) { + ASSERT(fds != NULL); + + if (fds->fdspoll) { + capwap_free(fds->fdspoll); + } + + if (fds->kmodevents) { + capwap_free(fds->kmodevents); + } +} + +/* */ +int ac_execute_update_fdspool(struct ac_fds* fds) { + int totalcount; + int kmodcount; + struct pollfd* fdsbuffer; + + ASSERT(fds != NULL); + + /* Retrieve number of Dynamic File Descriptor Event */ + kmodcount = ac_kmod_getfd(NULL, NULL, 0); + if (kmodcount < 0) { + return -1; + } + + /* Kernel Module Events Callback */ + fds->kmodeventsstartpos = -1; + if (kmodcount != fds->kmodeventscount) { + if (fds->kmodevents) { + capwap_free(fds->kmodevents); + } + + /* */ + fds->kmodeventscount = kmodcount; + fds->kmodevents = (struct ac_kmod_event*)((kmodcount > 0) ? capwap_alloc(sizeof(struct ac_kmod_event) * kmodcount) : NULL); + } + + /* Resize poll */ + totalcount = fds->fdsnetworkcount + fds->msgqueuecount + fds->kmodeventscount; + if (fds->fdstotalcount != totalcount) { + fdsbuffer = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * totalcount); + if (fds->fdspoll) { + int count = fds->fdsnetworkcount + fds->msgqueuecount; + if (count > 0) { + memcpy(fdsbuffer, fds->fdspoll, sizeof(struct pollfd) * count); + } + + capwap_free(fds->fdspoll); + } + + /* */ + fds->fdspoll = fdsbuffer; + fds->fdstotalcount = totalcount; + } + + /* Retrieve File Descriptor Kernel Module Event */ + if (fds->kmodeventscount > 0) { + fds->kmodeventsstartpos = fds->fdsnetworkcount + fds->msgqueuecount; + ac_kmod_getfd(&fds->fdspoll[fds->kmodeventsstartpos], fds->kmodevents, fds->kmodeventscount); + } + + return fds->fdstotalcount; +} + /* AC running */ int ac_execute(void) { - struct pollfd* fds; int result = CAPWAP_SUCCESSFUL; - int fdscount = CAPWAP_MAX_SOCKETS * 2 + 1; int index; int check; - int isctrlsocket = 0; + struct capwap_socket socket; struct sockaddr_storage recvfromaddr; struct sockaddr_storage recvtoaddr; char buffer[CAPWAP_MAX_PACKET_SIZE]; int buffersize; - /* Configure poll struct */ - fds = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * fdscount); - - /* Retrive all socket for polling */ - fdscount = capwap_network_set_pollfd(&g_ac.net, fds, fdscount); - ASSERT(fdscount > 0); - - /* Unix socket message queue */ - fds[fdscount].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; - fds[fdscount].fd = g_ac.fdmsgsessions[1]; - fdscount++; + /* Set file descriptor pool */ + if (ac_execute_init_fdspool(&g_ac.fds, &g_ac.net, g_ac.fdmsgsessions[1]) <= 0) { + capwap_logging_debug("Unable to initialize file descriptor pool"); + return AC_ERROR_SYSTEM_FAILER; + } /* Handler signal */ g_ac.running = 1; @@ -738,14 +837,14 @@ int ac_execute(void) { /* Start discovery thread */ if (!ac_discovery_start()) { - capwap_free(fds); + ac_execute_free_fdspool(&g_ac.fds); capwap_logging_debug("Unable to start discovery thread"); return AC_ERROR_SYSTEM_FAILER; } /* Enable Backend Management */ if (!ac_backend_start()) { - capwap_free(fds); + ac_execute_free_fdspool(&g_ac.fds); ac_discovery_stop(); capwap_logging_error("Unable start backend management"); return AC_ERROR_SYSTEM_FAILER; @@ -755,7 +854,7 @@ int ac_execute(void) { while (g_ac.running) { /* Receive packet */ buffersize = sizeof(buffer); - index = ac_recvfrom(fds, fdscount, buffer, &buffersize, &recvfromaddr, &recvtoaddr); + index = ac_recvfrom(&g_ac.fds, buffer, &buffersize, &recvfromaddr, &recvtoaddr); if (!g_ac.running) { capwap_logging_debug("Closing AC"); break; @@ -770,7 +869,7 @@ int ac_execute(void) { socklen_t sockinfolen = sizeof(struct sockaddr_storage); memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); - if (getsockname(fds[index].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + if (getsockname(g_ac.fds.fdspoll[index].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { break; } @@ -778,9 +877,11 @@ int ac_execute(void) { } } + /* Retrieve network information */ + capwap_get_network_socket(&g_ac.net, &socket, g_ac.fds.fdspoll[index].fd); + /* Search the AC session / session data */ - isctrlsocket = ((index < (fdscount / 2)) ? 1 : 0); - if (isctrlsocket) { + if (socket.isctrlsocket) { struct ac_session_t* session = ac_search_session_from_wtpaddress(&recvfromaddr); if (session) { @@ -813,15 +914,10 @@ int ac_execute(void) { unsigned long type = ntohl(control->type); if (type == CAPWAP_DISCOVERY_REQUEST) { - ac_discovery_add_packet(buffer, buffersize, fds[index].fd, &recvfromaddr); + ac_discovery_add_packet(buffer, buffersize, g_ac.fds.fdspoll[index].fd, &recvfromaddr); } else if (!g_ac.enabledtls && (type == CAPWAP_JOIN_REQUEST)) { - struct capwap_socket ctrlsock; - - /* Retrive socket info */ - capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd); - /* Create a new session */ - session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock); + session = ac_create_session(&recvfromaddr, &recvtoaddr, &socket); ac_session_add_packet(session, buffer, buffersize, 1); /* Release reference */ @@ -832,13 +928,8 @@ int ac_execute(void) { } else if (check == CAPWAP_DTLS_PACKET) { /* Before create new session check if receive DTLS Client Hello */ if (capwap_crypt_has_dtls_clienthello(&((char*)buffer)[sizeof(struct capwap_dtls_header)], buffersize - sizeof(struct capwap_dtls_header))) { - struct capwap_socket ctrlsock; - - /* Retrive socket info */ - capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd); - /* Create a new session */ - session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock); + session = ac_create_session(&recvfromaddr, &recvtoaddr, &socket); ac_session_add_packet(session, buffer, buffersize, 0); /* Release reference */ @@ -875,13 +966,8 @@ int ac_execute(void) { /* */ if (plain >= 0) { - struct capwap_socket datasock; - - /* Retrive socket info */ - capwap_get_network_socket(&g_ac.net, &datasock, fds[index].fd); - /* Create a new session */ - sessiondata = ac_create_session_data(&recvfromaddr, &recvtoaddr, &datasock, plain); + sessiondata = ac_create_session_data(&recvfromaddr, &recvtoaddr, &socket, plain); ac_session_data_add_packet(sessiondata, buffer, buffersize, 0); /* Release reference */ @@ -889,7 +975,7 @@ int ac_execute(void) { } } } - } else if ((index == CAPWAP_RECV_ERROR_INTR) || (index == AC_RECV_NOERROR_MSGQUEUE)) { + } else if ((index == CAPWAP_RECV_ERROR_INTR) || (index == AC_RECV_NOERROR_MSGQUEUE) || (index == AC_RECV_NOERROR_KMODEVENT)) { /* Ignore recv */ continue; } else if (index == CAPWAP_RECV_ERROR_SOCKET) { @@ -913,7 +999,7 @@ int ac_execute(void) { /* Free Backend Management */ ac_backend_free(); - /* Free memory */ - capwap_free(fds); + /* Free file description pool */ + ac_execute_free_fdspool(&g_ac.fds); return result; } diff --git a/src/ac/ac_kmod.c b/src/ac/ac_kmod.c new file mode 100644 index 0000000..86e7fb0 --- /dev/null +++ b/src/ac/ac_kmod.c @@ -0,0 +1,278 @@ +#include "ac.h" +#include +#include +#include +#include "nlsmartcapwap.h" + +/* Compatibility functions */ +#ifdef HAVE_LIBNL_10 +static uint32_t g_portbitmap[32] = { 0 }; + +static struct nl_sock* nl_socket_alloc_cb(void* cb) { + int i; + struct nl_sock* handle; + uint32_t pid = getpid() & 0x3FFFFF; + + handle = nl_handle_alloc_cb(cb); + for (i = 0; i < 1024; i++) { + if (g_portbitmap[i / 32] & (1 << (i % 32))) { + continue; + } + + g_portbitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + return handle; +} + +static void nl_socket_free(struct nl_sock* handle) { + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + g_portbitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} +#endif + +/* */ +typedef int (*ac_kmod_valid_cb)(struct nl_msg* msg, void* data); + +/* */ +static struct nl_sock* nl_create_handle(struct nl_cb* cb) { + struct nl_sock* handle; + + handle = nl_socket_alloc_cb(cb); + if (!handle) { + return NULL; + } + + if (genl_connect(handle)) { + nl_socket_free(handle); + return NULL; + } + + return handle; +} + +/* */ +static int ac_kmod_no_seq_check(struct nl_msg* msg, void* arg) { + return NL_OK; +} + +/* */ +static int ac_kmod_error_handler(struct sockaddr_nl* nla, struct nlmsgerr* err, void* arg) { + *((int*)arg) = err->error; + return NL_STOP; +} + +/* */ +static int ac_kmod_finish_handler(struct nl_msg* msg, void* arg) { + *((int*)arg) = 0; + return NL_SKIP; +} + +/* */ +static int ac_kmod_ack_handler(struct nl_msg* msg, void* arg) { + *((int*)arg) = 0; + return NL_STOP; +} + +/* */ +static int ac_kmod_event_handler(struct genlmsghdr* gnlh, struct nlattr** tb_msg, void* data) { + switch (gnlh->cmd) { + default: { + capwap_logging_debug("*** ac_kmod_event_handler: %d", (int)gnlh->cmd); + break; + } + } + + return NL_SKIP; +} + +/* */ +static int ac_kmod_valid_handler(struct nl_msg* msg, void* data) { + struct nlattr* tb_msg[NLSMARTCAPWAP_AC_ATTR_MAX + 1]; + struct genlmsghdr* gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NLSMARTCAPWAP_AC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + + return ac_kmod_event_handler(gnlh, tb_msg, data); +} + +/* */ +static int ac_kmod_send_and_recv(struct nl_sock* nl, struct nl_cb* nl_cb, struct nl_msg* msg, ac_kmod_valid_cb valid_cb, void* data) { + int result; + struct nl_cb* cb; + + /* Clone netlink callback */ + cb = nl_cb_clone(nl_cb); + if (!cb) { + return -1; + } + + /* Complete send message */ + result = nl_send_auto_complete(nl, msg); + if (result < 0) { + nl_cb_put(cb); + return -1; + } + + /* Customize message callback */ + nl_cb_err(cb, NL_CB_CUSTOM, ac_kmod_error_handler, &result); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, ac_kmod_finish_handler, &result); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ac_kmod_ack_handler, &result); + + if (valid_cb) { + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_cb, data); + } + + result = 1; + while (result > 0) { + nl_recvmsgs(nl, cb); + } + + nl_cb_put(cb); + return result; +} + +/* */ +static int ac_kmod_send_and_recv_msg(struct nl_msg* msg, ac_kmod_valid_cb valid_cb, void* data) { + return ac_kmod_send_and_recv(g_ac.kmodhandle.nl, g_ac.kmodhandle.nl_cb, msg, valid_cb, data); +} + +/* */ +static int ac_kmod_link(void) { + int result; + struct nl_msg* msg; + + /* */ + msg = nlmsg_alloc(); + if (!msg) { + return -1; + } + + /* */ + genlmsg_put(msg, 0, 0, g_ac.kmodhandle.nlsmartcapwap_id, 0, 0, NLSMARTCAPWAP_AC_CMD_LINK, 0); + + /* */ + result = ac_kmod_send_and_recv_msg(msg, NULL, NULL); + if (result) { + if (result == -EALREADY) { + result = 0; + } else { + capwap_logging_warning("Unable to connect kernel module, error code: %d", result); + } + } + + /* */ + nlmsg_free(msg); + return result; +} + +/* */ +static void ac_kmod_event_receive(int fd, void** params, int paramscount) { + int res; + + ASSERT(fd >= 0); + ASSERT(params != NULL); + ASSERT(paramscount == 2); + + /* */ + res = nl_recvmsgs((struct nl_sock*)params[0], (struct nl_cb*)params[1]); + if (res) { + capwap_logging_warning("Receive kernel module message failed: %d", res); + } +} + +/* */ +int ac_kmod_isconnected(void) { + return (g_ac.kmodhandle.nlsmartcapwap_id ? 1 : 0); +} + +/* */ +int ac_kmod_getfd(struct pollfd* fds, struct ac_kmod_event* events, int count) { + int kmodcount = (ac_kmod_isconnected() ? 1 : 0); + + /* */ + if (!kmodcount) { + return 0; + } else if (!fds && !events && !count) { + return kmodcount; + } else if ((count > 0) && (!fds || !events)) { + return -1; + } else if (count < kmodcount) { + return -1; + } + + /* */ + fds[0].fd = g_ac.kmodhandle.nl_fd; + fds[0].events = POLLIN | POLLERR | POLLHUP; + + /* */ + events[0].event_handler = ac_kmod_event_receive; + events[0].params[0] = (void*)g_ac.kmodhandle.nl; + events[0].params[1] = (void*)g_ac.kmodhandle.nl_cb; + events[0].paramscount = 2; + + return kmodcount; +} + +/* */ +int ac_kmod_init(void) { + int result; + + /* Configure netlink callback */ + g_ac.kmodhandle.nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!g_ac.kmodhandle.nl_cb) { + ac_kmod_free(); + return -1; + } + + /* Create netlink socket */ + g_ac.kmodhandle.nl = nl_create_handle(g_ac.kmodhandle.nl_cb); + if (!g_ac.kmodhandle.nl) { + ac_kmod_free(); + return -1; + } + + g_ac.kmodhandle.nl_fd = nl_socket_get_fd(g_ac.kmodhandle.nl); + + /* Get nlsmartcapwap netlink family */ + g_ac.kmodhandle.nlsmartcapwap_id = genl_ctrl_resolve(g_ac.kmodhandle.nl, SMARTCAPWAP_AC_GENL_NAME); + if (g_ac.kmodhandle.nlsmartcapwap_id < 0) { + capwap_logging_warning("Unable to found kernel module"); + ac_kmod_free(); + return -1; + } + + /* Configure callback function */ + nl_cb_set(g_ac.kmodhandle.nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, ac_kmod_no_seq_check, NULL); + nl_cb_set(g_ac.kmodhandle.nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ac_kmod_valid_handler, NULL); + + /* Link to kernel module */ + result = ac_kmod_link(); + if (result) { + ac_kmod_free(); + return result; + } + + return 0; +} + +/* */ +void ac_kmod_free(void) { + if (g_ac.kmodhandle.nl) { + nl_socket_free(g_ac.kmodhandle.nl); + } + + if (g_ac.kmodhandle.nl_cb) { + nl_cb_put(g_ac.kmodhandle.nl_cb); + } + + /* */ + memset(&g_ac.kmodhandle, 0, sizeof(struct ac_kmod_handle)); +} diff --git a/src/ac/ac_kmod.h b/src/ac/ac_kmod.h new file mode 100644 index 0000000..704e599 --- /dev/null +++ b/src/ac/ac_kmod.h @@ -0,0 +1,42 @@ +#ifndef __AC_KMOD_HEADER__ +#define __AC_KMOD_HEADER__ + +/* */ +#ifdef HAVE_LIBNL_10 +#define nl_sock nl_handle +#endif + +/* */ +#define AC_KMOD_MODE_LOCAL 0x00000001 +#define AC_KMOD_MODE_TUNNEL_USERMODE 0x00000002 +#define AC_KMOD_MODE_TUNNEL_KERNELMODE 0x00000003 + +/* */ +#define AC_KMOD_FLAGS_TUNNEL_NATIVE 0x00000000 +#define AC_KMOD_FLAGS_TUNNEL_8023 0x00000001 + +/* */ +struct ac_kmod_handle { + struct nl_sock* nl; + int nl_fd; + struct nl_cb* nl_cb; + int nlsmartcapwap_id; +}; + +/* */ +#define AC_KMOD_EVENT_MAX_ITEMS 2 +struct ac_kmod_event { + void (*event_handler)(int fd, void** params, int paramscount); + int paramscount; + void* params[AC_KMOD_EVENT_MAX_ITEMS]; +}; + +/* */ +int ac_kmod_init(void); +void ac_kmod_free(void); + +/* */ +int ac_kmod_isconnected(void); +int ac_kmod_getfd(struct pollfd* fds, struct ac_kmod_event* events, int count); + +#endif /* __AC_KMOD_HEADER__ */ diff --git a/src/ac/kmod/Makefile b/src/ac/kmod/Makefile new file mode 100644 index 0000000..3fbd74b --- /dev/null +++ b/src/ac/kmod/Makefile @@ -0,0 +1,13 @@ +KVERSION = $(shell uname -r) + +obj-m += smartcapwap.o + +smartcapwap-y := \ + main.o \ + netlinkapp.o + +all: + make -C /lib/modules/$(KVERSION)/build M="$(PWD)" modules + +clean: + make -C /lib/modules/$(KVERSION)/build M="$(PWD)" clean diff --git a/src/ac/kmod/main.c b/src/ac/kmod/main.c new file mode 100644 index 0000000..b086560 --- /dev/null +++ b/src/ac/kmod/main.c @@ -0,0 +1,24 @@ +#include +#include +#include "netlinkapp.h" + +/* */ +static int __init smartcapwap_ac_init(void) { + int result = 0; + + /* */ + result = nlsmartcapwap_ac_init(); + + return result; +} +module_init(smartcapwap_ac_init); + +/* */ +static void __exit smartcapwap_ac_exit(void) { + nlsmartcapwap_ac_exit(); +} +module_exit(smartcapwap_ac_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Massimo Vellucci "); +MODULE_DESCRIPTION("SmartCAPWAP AC Data Channel Module"); diff --git a/src/ac/kmod/netlinkapp.c b/src/ac/kmod/netlinkapp.c new file mode 100644 index 0000000..c8caa54 --- /dev/null +++ b/src/ac/kmod/netlinkapp.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nlsmartcapwap.h" +#include "netlinkapp.h" + +/* */ +struct nlsmartcapwap_ac_device { + struct list_head list; + + u32 usermodeid; +}; + +/* */ +static u32 nlsmartcapwap_ac_usermodeid = 0; +static LIST_HEAD(nlsmartcapwap_ac_dev_list); + +/* Netlink Family */ +static struct genl_family nlsmartcapwap_ac_family = { + .id = GENL_ID_GENERATE, + .name = SMARTCAPWAP_AC_GENL_NAME, + .hdrsize = 0, + .version = 1, + .maxattr = NLSMARTCAPWAP_AC_ATTR_MAX, + .netnsok = true, +}; + +/* */ +static void nlsmartcapwap_ac_free_device(struct nlsmartcapwap_ac_device* nldev) { + /* Free memory */ + kfree(nldev); +} + +/* */ +static void nlsmartcapwap_ac_close(void) { + struct nlsmartcapwap_ac_device* nldev; + struct nlsmartcapwap_ac_device* tmp; + + list_for_each_entry_safe(nldev, tmp, &nlsmartcapwap_ac_dev_list, list) { + list_del(&nldev->list); + + /* Free device */ + nlsmartcapwap_ac_free_device(nldev); + } +} + + +/* */ +static int nlsmartcapwap_ac_link(struct sk_buff* skb, struct genl_info* info) { + int ret = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + u32 portid = info->snd_pid; +#else + u32 portid = info->snd_portid; +#endif + + rtnl_lock(); + + if (!nlsmartcapwap_ac_usermodeid) { + nlsmartcapwap_ac_usermodeid = portid; + } else if (nlsmartcapwap_ac_usermodeid == portid) { + ret = -EALREADY; + } else { + ret = -EBUSY; + } + + rtnl_unlock(); + + return ret; +} + +/* */ +static int nlsmartcapwap_ac_netlink_notify(struct notifier_block* nb, unsigned long state, void* _notify) { + struct netlink_notify* notify = (struct netlink_notify*)_notify; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + u32 portid = notify->pid; +#else + u32 portid = notify->portid; +#endif + + /* */ + if (state == NETLINK_URELEASE) { + rtnl_lock(); + + if (nlsmartcapwap_ac_usermodeid == portid) { + nlsmartcapwap_ac_usermodeid = 0; + + /* Close all devices */ + nlsmartcapwap_ac_close(); + } + + rtnl_unlock(); + } + + return NOTIFY_DONE; +} + +/* */ +static const struct nla_policy nlsmartcapwap_ac_policy[NLSMARTCAPWAP_AC_ATTR_MAX + 1] = { + [NLSMARTCAPWAP_AC_ATTR_FLAGS] = { .type = NLA_U32 }, +}; + +/* Netlink Ops */ +static struct genl_ops nlsmartcapwap_ac_ops[] = { + { + .cmd = NLSMARTCAPWAP_AC_CMD_LINK, + .doit = nlsmartcapwap_ac_link, + .policy = nlsmartcapwap_ac_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + +/* Netlink notify */ +static struct notifier_block nlsmartcapwap_ac_netlink_notifier = { + .notifier_call = nlsmartcapwap_ac_netlink_notify, +}; + +/* */ +int nlsmartcapwap_ac_init(void) { + int ret; + + /* Register netlink family */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) + ret = genl_register_family_with_ops(&nlsmartcapwap_ac_family, nlsmartcapwap_ac_ops, sizeof(nlsmartcapwap_ac_ops) / sizeof(nlsmartcapwap_ac_ops[0])); +#else + ret = genl_register_family_with_ops(&nlsmartcapwap_ac_family, nlsmartcapwap_ac_ops); +#endif + if (ret) { + return ret; + } + + /* Register netlink notifier */ + ret = netlink_register_notifier(&nlsmartcapwap_ac_netlink_notifier); + if (ret) { + genl_unregister_family(&nlsmartcapwap_ac_family); + return ret; + } + + return ret; +} + +/* */ +void nlsmartcapwap_ac_exit(void) { + /* */ + rtnl_lock(); + nlsmartcapwap_ac_close(); + rtnl_unlock(); + + /* */ + netlink_unregister_notifier(&nlsmartcapwap_ac_netlink_notifier); + genl_unregister_family(&nlsmartcapwap_ac_family); +} diff --git a/src/ac/kmod/netlinkapp.h b/src/ac/kmod/netlinkapp.h new file mode 100644 index 0000000..8983134 --- /dev/null +++ b/src/ac/kmod/netlinkapp.h @@ -0,0 +1,8 @@ +#ifndef __KMOD_AC_NETLINKAPP_HEADER__ +#define __KMOD_AC_NETLINKAPP_HEADER__ + +/* */ +int nlsmartcapwap_ac_init(void); +void nlsmartcapwap_ac_exit(void); + +#endif /* __KMOD_AC_NETLINKAPP_HEADER__ */ diff --git a/src/ac/kmod/nlsmartcapwap.h b/src/ac/kmod/nlsmartcapwap.h new file mode 100644 index 0000000..ad74142 --- /dev/null +++ b/src/ac/kmod/nlsmartcapwap.h @@ -0,0 +1,29 @@ +#ifndef __AC_NLSMARTCAPWAP_HEADER__ +#define __AC_NLSMARTCAPWAP_HEADER__ + +/* */ +#define SMARTCAPWAP_AC_GENL_NAME "smartcapwap_ac" + +/* */ +enum nlsmartcapwap_ac_attrs { + NLSMARTCAPWAP_AC_ATTR_UNSPEC, + + NLSMARTCAPWAP_AC_ATTR_FLAGS, + + /* Last attribute */ + __NLSMARTCAPWAP_AC_ATTR_AFTER_LAST, + NLSMARTCAPWAP_AC_ATTR_MAX = __NLSMARTCAPWAP_AC_ATTR_AFTER_LAST - 1 +}; + +/* */ +enum nlsmartcapwap_ac_commands { + NLSMARTCAPWAP_AC_CMD_UNSPEC, + + NLSMARTCAPWAP_AC_CMD_LINK, + + /* Last command */ + __NLSMARTCAPWAP_AC_CMD_AFTER_LAST, + NLSMARTCAPWAP_AC_CMD_MAX = __NLSMARTCAPWAP_AC_CMD_AFTER_LAST - 1 +}; + +#endif /* __AC_NLSMARTCAPWAP_HEADER__ */ diff --git a/src/wtp/wtp_dfa.c b/src/wtp/wtp_dfa.c index 21c9f54..ee959e9 100644 --- a/src/wtp/wtp_dfa.c +++ b/src/wtp/wtp_dfa.c @@ -239,7 +239,7 @@ int wtp_dfa_update_fdspool(struct wtp_fds* fds) { /* */ fds->kmodeventscount = kmodcount; - fds->kmodevents = (struct wtp_kmod_event*)((wificount > 0) ? capwap_alloc(sizeof(struct wtp_kmod_event) * kmodcount) : NULL); + fds->kmodevents = (struct wtp_kmod_event*)((kmodcount > 0) ? capwap_alloc(sizeof(struct wtp_kmod_event) * kmodcount) : NULL); } /* Wifi Events Callback */ @@ -271,7 +271,6 @@ int wtp_dfa_update_fdspool(struct wtp_fds* fds) { fds->fdstotalcount = totalcount; } - /* Retrieve File Descriptor Kernel Module Event */ if (fds->kmodeventscount > 0) { fds->kmodeventsstartpos = fds->fdsnetworkcount; diff --git a/src/wtp/wtp_kmod.c b/src/wtp/wtp_kmod.c index 5704d26..7b73c6c 100644 --- a/src/wtp/wtp_kmod.c +++ b/src/wtp/wtp_kmod.c @@ -299,10 +299,15 @@ int wtp_kmod_join_mac80211_device(struct wifi_wlan* wlan, uint32_t mode, uint32_ /* */ result = wtp_kmod_send_and_recv(interface->nl, interface->nl_cb, msg, NULL, NULL); if (!result) { + interface->mode = mode; + interface->flags = flags; interface->wlan = wlan; /* */ capwap_itemlist_insert_after(g_wtp.kmodhandle.interfaces, NULL, itemlist); + if (mode == WTP_KMOD_MODE_TUNNEL_USERMODE) { + g_wtp.kmodhandle.interfaceconnectioncount++; + } } else { nl_socket_free(interface->nl); nl_cb_put(interface->nl_cb); @@ -349,6 +354,10 @@ int wtp_kmod_leave_mac80211_device(struct wifi_wlan* wlan) { nl_cb_put(interface->nl_cb); /* Free item */ + if (interface->mode == WTP_KMOD_MODE_TUNNEL_USERMODE) { + g_wtp.kmodhandle.interfaceconnectioncount--; + } + capwap_itemlist_free(capwap_itemlist_remove(g_wtp.kmodhandle.interfaces, itemlist)); break; } @@ -369,19 +378,7 @@ int wtp_kmod_isconnected(void) { int wtp_kmod_getfd(struct pollfd* fds, struct wtp_kmod_event* events, int count) { int i; struct capwap_list_item* itemlist; - int kmodcount = 0; - - /* */ - if (wtp_kmod_isconnected()) { - kmodcount = 1; - for (itemlist = g_wtp.kmodhandle.interfaces->first; itemlist; itemlist = itemlist->next) { - struct wtp_kmod_iface_handle* interface = (struct wtp_kmod_iface_handle*)itemlist->item; - - if (interface->nl_fd) { - kmodcount++; - } - } - } + int kmodcount = (wtp_kmod_isconnected() ? 1 + g_wtp.kmodhandle.interfaceconnectioncount : 0); /* */ if (!kmodcount) { diff --git a/src/wtp/wtp_kmod.h b/src/wtp/wtp_kmod.h index cf98f9c..41cb69a 100644 --- a/src/wtp/wtp_kmod.h +++ b/src/wtp/wtp_kmod.h @@ -23,6 +23,8 @@ struct wtp_kmod_iface_handle { int nl_fd; struct nl_cb* nl_cb; + uint32_t mode; + uint32_t flags; struct wifi_wlan* wlan; }; @@ -35,6 +37,7 @@ struct wtp_kmod_handle { /* */ struct capwap_list* interfaces; + int interfaceconnectioncount; }; /* */