From: Daniel Wagner <[email protected]>

Instead of factoring out common parts from rtnl.c we add a
new implementation for handling the upcoming ACCT netfilter netlink
messages.

The main reason is not to disturb the working rtnl code (although
it has several hidden bugs, which are not triggered by the ussage
pattern). The idea is to wait for ELL and then rip out the common
netlink code.
---
 Makefile.am     |   2 +-
 src/connman.h   |   4 +
 src/main.c      |   2 +
 src/netfilter.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 src/netfilter.c

diff --git a/Makefile.am b/Makefile.am
index 78b1b33..e42be93 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -97,7 +97,7 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) 
$(gweb_sources) \
                        src/session.c src/tethering.c src/wpad.c src/wispr.c \
                        src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
                        src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
-                       src/inotify.c src/firewall.c
+                       src/inotify.c src/firewall.c src/netfilter.c
 
 src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
                                @XTABLES_LIBS@ @GNUTLS_LIBS@ -lresolv -ldl -lrt
diff --git a/src/connman.h b/src/connman.h
index 0467e9e..e0546d6 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -897,3 +897,7 @@ int __connman_firewall_disable(struct firewall_context 
*ctx);
 
 int __connman_firewall_init(void);
 void __connman_firewall_cleanup(void);
+
+
+int __connman_netfilter_init(void);
+void __connman_netfilter_cleanup(void);
diff --git a/src/main.c b/src/main.c
index a76ec3b..c10b376 100644
--- a/src/main.c
+++ b/src/main.c
@@ -634,6 +634,7 @@ int main(int argc, char *argv[])
        __connman_ippool_init();
        __connman_iptables_init();
        __connman_firewall_init();
+       __connman_netfilter_init();
        __connman_nat_init();
        __connman_tethering_init();
        __connman_counter_init();
@@ -694,6 +695,7 @@ int main(int argc, char *argv[])
        __connman_counter_cleanup();
        __connman_tethering_cleanup();
        __connman_nat_cleanup();
+       __connman_netfilter_cleanup();
        __connman_firewall_cleanup();
        __connman_iptables_cleanup();
        __connman_ippool_cleanup();
diff --git a/src/netfilter.c b/src/netfilter.c
new file mode 100644
index 0000000..63aa8fc
--- /dev/null
+++ b/src/netfilter.c
@@ -0,0 +1,260 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2013  BWM CarIT GmbH. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static GIOChannel *channel;
+
+static GSList *request_list;
+
+typedef void (*connman_netlink_handler_cb_t) (int error,
+                                               uint16_t type, const void *data,
+                                               uint32_t len, void *user_data);
+
+static struct cb_data *find_request(guint32 seq)
+{
+       GSList *list;
+
+       for (list = request_list; list; list = list->next) {
+               struct cb_data *cbd = list->data;
+               struct nlmsghdr *hdr = cbd->data;
+
+               if (hdr->nlmsg_seq == seq)
+                       return cbd;
+       }
+
+       return NULL;
+}
+
+static const char *type2string(uint16_t type)
+{
+       switch (type) {
+       case NLMSG_NOOP:
+               return "NOOP";
+       case NLMSG_ERROR:
+               return "ERROR";
+       case NLMSG_DONE:
+               return "DONE";
+       case NLMSG_OVERRUN:
+               return "OVERRUN";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+static int send_request(struct nlmsghdr *hdr)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+
+       return sendto(sk, hdr, hdr->nlmsg_len, 0,
+                       (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static void remove_request(struct cb_data *cbd)
+{
+       struct nlmsghdr *hdr = cbd->data;
+
+       DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
+
+       request_list = g_slist_remove(request_list, cbd);
+
+       g_free(hdr);
+       g_free(cbd);
+
+       /*
+        * Now after finishing up with the previous command
+        * we can send the next one.
+        */
+       cbd = g_slist_nth_data(request_list, 0);
+       if (cbd != NULL)
+               send_request(cbd->data);
+}
+
+static void process_message(void *buf, size_t len)
+{
+       struct cb_data *cbd;
+       connman_netlink_handler_cb_t cb;
+       struct nlmsghdr *nlmsg;
+       struct nlmsgerr *err;
+
+       DBG("buf %p len %zd", buf, len);
+
+       for (nlmsg = buf; NLMSG_OK(nlmsg, len);
+                       nlmsg = NLMSG_NEXT(nlmsg, len)) {
+
+               DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(nlmsg->nlmsg_type),
+                               nlmsg->nlmsg_len, nlmsg->nlmsg_type,
+                               nlmsg->nlmsg_flags, nlmsg->nlmsg_seq);
+
+               cbd = find_request(nlmsg->nlmsg_seq);
+               if (cbd == NULL)
+                       continue;
+
+               cb = cbd->cb;
+
+               switch (nlmsg->nlmsg_type) {
+               case NLMSG_ERROR:
+                       err = NLMSG_DATA(nlmsg);
+                       DBG("error %d (%s)", -err->error,
+                                       strerror(-err->error));
+                       cb(err->error, 0, NULL, 0, cbd->user_data);
+
+                       remove_request(cbd);
+                       continue;
+
+               case NLMSG_NOOP:
+               case NLMSG_OVERRUN:
+               case NLMSG_DONE:
+                       cb(0, nlmsg->nlmsg_type, NLMSG_DATA(nlmsg),
+                               nlmsg->nlmsg_len - NLMSG_HDRLEN,
+                               cbd->user_data);
+
+                       remove_request(cbd);
+                       continue;
+               }
+
+               cb(0, nlmsg->nlmsg_type, NLMSG_DATA(nlmsg),
+                       nlmsg->nlmsg_len - NLMSG_HDRLEN,
+                       cbd->user_data);
+       }
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[4096];
+       struct sockaddr_nl nladdr;
+       socklen_t addr_len = sizeof(nladdr);
+       ssize_t status;
+       int fd;
+
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       memset(buf, 0, sizeof(buf));
+       memset(&nladdr, 0, sizeof(nladdr));
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+       if (status < 0) {
+               if (errno == EINTR || errno == EAGAIN)
+                       return TRUE;
+
+               return FALSE;
+       }
+
+       if (status == 0)
+               return FALSE;
+
+       if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+               DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+               return TRUE;
+       }
+
+       process_message(buf, status);
+
+       return TRUE;
+}
+
+static void cleanup_request_list(gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct nlmsghdr *hdr = cbd->data;
+
+       g_free(hdr);
+       g_free(cbd);
+}
+
+int __connman_netfilter_init(void)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("");
+
+       sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_NETFILTER);
+       if (sk < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = 0;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       channel = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       netlink_event, NULL);
+
+       return 0;
+}
+
+void __connman_netfilter_cleanup(void)
+{
+       DBG("");
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       g_slist_free_full(request_list, cleanup_request_list);
+       request_list = NULL;
+}
-- 
1.8.2.rc3.16.gce432ca

_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to