From: Daniel Wagner <[email protected]>
---
src/connman.h | 31 +++++
src/netfilter.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 389 insertions(+)
diff --git a/src/connman.h b/src/connman.h
index e0546d6..80a5336 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -898,6 +898,37 @@ int __connman_firewall_disable(struct firewall_context
*ctx);
int __connman_firewall_init(void);
void __connman_firewall_cleanup(void);
+typedef void (*connman_netfilter_acct_new_cb_t) (int error, void *user_data);
+
+typedef void (*connman_netfilter_acct_get_cb_t) (int error, const char *name,
+ uint64_t packets,
+ uint64_t bytes,
+ void *user_data);
+
+typedef void (*connman_netfilter_acct_dump_cb_t) (int error,
+ const char *name,
+ uint64_t packets,
+ uint64_t bytes,
+ void *user_data);
+
+typedef void (*connman_netfilter_acct_del_cb_t) (int error,
+ void *user_data);
+
+
+unsigned int __connman_netfilter_acct_new(const char *name,
+ connman_netfilter_acct_new_cb_t cb,
+ void *user_data);
+unsigned int __connman_netfilter_acct_dump(connman_bool_t zero,
+ connman_netfilter_acct_dump_cb_t cb,
+ void *user_data);
+unsigned int __connman_netfilter_acct_get(const char *name, connman_bool_t
zero,
+ connman_netfilter_acct_get_cb_t cb,
+ void *user_data);
+unsigned int __connman_netfilter_acct_del(const char *name,
+ connman_netfilter_acct_del_cb_t cb,
+ void *user_data);
+
+void __connman_netfilter_cancel(unsigned int id);
int __connman_netfilter_init(void);
void __connman_netfilter_cleanup(void);
diff --git a/src/netfilter.c b/src/netfilter.c
index 63aa8fc..863e949 100644
--- a/src/netfilter.c
+++ b/src/netfilter.c
@@ -29,15 +29,30 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <linux/genetlink.h>
#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_acct.h>
#include <gdbus.h>
#include "connman.h"
+#define NFMSG_LEN(len) (NLMSG_HDRLEN + NLMSG_ALIGN(GENL_HDRLEN + (len)))
+#define NFGEN_DATA(nlh) ((void *)((char *)(nlh) +
\
+ NLMSG_ALIGN(sizeof(struct nfgenmsg))))
+#define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN))
+#define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) &&
\
+ (nla)->nla_len >= sizeof(struct nlattr) && \
+ (nla)->nla_len <= (len))
+#define NLA_NEXT(nla,attrlen) ((attrlen) -= NLA_ALIGN((nla)->nla_len), \
+ (struct nlattr*)(((char*)(nla)) + \
+ NLA_ALIGN((nla)->nla_len)))
+
static GIOChannel *channel;
static GSList *request_list;
+static guint32 request_seq = 1;
typedef void (*connman_netlink_handler_cb_t) (int error,
uint16_t type, const void *data,
@@ -69,6 +84,14 @@ static const char *type2string(uint16_t type)
return "DONE";
case NLMSG_OVERRUN:
return "OVERRUN";
+ case (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_NEW:
+ return "ACCT NEW";
+ case (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_GET:
+ return "ACCT GET";
+ case (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_GET_CTRZERO:
+ return "ACCT GET CTRZERO";
+ case (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_DEL:
+ return "ACCT DEL";
default:
return "UNKNOWN";
}
@@ -93,6 +116,341 @@ static int send_request(struct nlmsghdr *hdr)
(struct sockaddr *) &addr, sizeof(addr));
}
+static unsigned int queue_request(struct nlmsghdr *hdr, void *cb,
+ void *user_data)
+{
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ int err;
+
+ cbd->data = hdr;
+
+ 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_append(request_list, cbd);
+ if (g_slist_length(request_list) > 1)
+ goto out;
+
+ err = send_request(hdr);
+ if (err < 0) {
+ request_list = g_slist_remove(request_list, cbd);
+ g_free(hdr);
+ g_free(cbd);
+ return 0;
+ }
+
+out:
+ return hdr->nlmsg_seq;
+}
+
+static void parse_nlattr_acct(const struct nlattr *attr,
+ char **name, uint64_t *packets, uint64_t *bytes)
+{
+ switch (attr->nla_type) {
+ case NFACCT_NAME:
+ *name = NLA_DATA(attr);
+ break;
+ case NFACCT_PKTS:
+ *packets = be64toh(*(uint64_t *) NLA_DATA(attr));
+ break;
+ case NFACCT_BYTES:
+ *bytes = be64toh(*(uint64_t *) NLA_DATA(attr));
+ break;
+ case NFACCT_USE:
+ /* ignored */
+ break;
+ }
+}
+
+static struct nlmsghdr *create_nf_message(uint16_t type,
+ int16_t flags, size_t size)
+{
+ struct nlmsghdr *hdr;
+ struct nfgenmsg *msg;
+
+ hdr = g_try_malloc0(size);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->nlmsg_len = size;
+ hdr->nlmsg_type = type;
+ hdr->nlmsg_flags = flags;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
+
+ msg = NLMSG_DATA(hdr);
+ msg->nfgen_family = AF_UNSPEC;
+ msg->version = NFNETLINK_V0;
+ msg->res_id = 0;
+
+ return hdr;
+}
+
+static int append_attr_str(struct nlattr *attr,
+ uint16_t type, size_t size, const char *str)
+{
+ char *dst;
+
+ attr->nla_len = NLA_HDRLEN + size;
+ attr->nla_type = NFACCT_NAME;
+
+ dst = (char *)NLA_DATA(attr);
+ strncpy(dst, str, size);
+ dst[size - 1] = '\0';
+
+ return 0;
+}
+
+static int nfacct_new_cb(int error, uint16_t type, const void *data,
+ uint32_t len, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ connman_netfilter_acct_new_cb_t cb = cbd->cb;
+
+ DBG("type %d len %d", type, len);
+
+ cb(error, cbd->user_data);
+
+ g_free(cbd);
+
+ return 0;
+}
+
+unsigned int __connman_netfilter_acct_new(const char *name,
+ connman_netfilter_acct_new_cb_t cb,
+ void *user_data)
+{
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ struct nlmsghdr *nlmsg;
+ struct nlattr *attr;
+ int len, attrlen;
+ unsigned int id;
+
+ DBG("");
+
+ attrlen = strlen(name) + 1;
+ if (attrlen > NFACCT_NAME_MAX)
+ attrlen = NFACCT_NAME_MAX;
+
+ len = NFMSG_LEN(NLA_HDRLEN + attrlen);
+ nlmsg = create_nf_message(
+ (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_NEW,
+ NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK, len);
+ if (nlmsg == NULL) {
+ g_free(cbd);
+ return 0;
+ }
+
+ attr = NFGEN_DATA(NLMSG_DATA(nlmsg));
+ append_attr_str(attr, NFACCT_NAME, attrlen, name);
+
+ id = queue_request(nlmsg, nfacct_new_cb, cbd);
+ if (id == 0) {
+ g_free(nlmsg);
+ g_free(cbd);
+ }
+
+ return id;
+}
+
+static int nfacct_dump_cb(int error, uint16_t type, const void *data,
+ uint32_t len, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ const struct nfgenmsg *nfgen = data;
+ connman_netfilter_acct_dump_cb_t cb = cbd->cb;
+ const struct nlattr *attr;
+ uint64_t packets = 0, bytes = 0;
+ char *name = NULL;
+ int attrlen;
+
+ DBG("type %d len %d", type, len);
+
+ if (error < 0)
+ goto done;
+
+ attrlen = len - NLMSG_ALIGN(sizeof(struct nfgenmsg));
+
+ for (attr = NFGEN_DATA(nfgen); NLA_OK(attr, attrlen);
+ attr = NLA_NEXT(attr, attrlen))
+ parse_nlattr_acct(attr, &name, &packets, &bytes);
+
+done:
+ cb(error, name, packets, bytes, cbd->user_data);
+
+ if (type < NLMSG_MIN_TYPE)
+ g_free(cbd);
+
+ return 0;
+}
+
+unsigned int __connman_netfilter_acct_dump(connman_bool_t zero,
+ connman_netfilter_acct_dump_cb_t cb,
+ void *user_data)
+{
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ struct nlmsghdr *nlmsg;
+ unsigned int id;
+ uint16_t cmd;
+ int len;
+
+ DBG("");
+
+ if (zero == FALSE)
+ cmd = NFNL_MSG_ACCT_GET;
+ else
+ cmd = NFNL_MSG_ACCT_GET_CTRZERO;
+
+ len = NFMSG_LEN(0);
+ nlmsg = create_nf_message(
+ (NFNL_SUBSYS_ACCT << 8) | cmd,
+ NLM_F_REQUEST | NLM_F_DUMP, len);
+ if (nlmsg == NULL) {
+ g_free(cbd);
+ return 0;
+ }
+
+ id = queue_request(nlmsg, nfacct_dump_cb, cbd);
+ if (id == 0) {
+ g_free(nlmsg);
+ g_free(cbd);
+ }
+
+ return id;
+}
+
+static int nfacct_get_cb(int error, uint16_t type, const void *data,
+ uint32_t len, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ const struct nfgenmsg *nfgen = data;
+ connman_netfilter_acct_get_cb_t cb = cbd->cb;
+ const struct nlattr *attr;
+ uint64_t packets = 0, bytes = 0;
+ char *name = NULL;
+ int attrlen;
+
+ DBG("type %d len %d", type, len);
+
+ if (error < 0) {
+ cb(error, NULL, 0, 0, cbd->user_data);
+ g_free(cbd);
+ return 0;
+ }
+
+ if (type < NLMSG_MIN_TYPE) {
+ g_free(cbd);
+ return 0;
+ }
+
+ attrlen = len - NLMSG_ALIGN(sizeof(struct nfgenmsg));
+
+ for (attr = NFGEN_DATA(nfgen); NLA_OK(attr, attrlen);
+ attr = NLA_NEXT(attr, attrlen))
+ parse_nlattr_acct(attr, &name, &packets, &bytes);
+
+ cb(error, name, packets, bytes, cbd->user_data);
+
+ return 0;
+}
+
+unsigned int __connman_netfilter_acct_get(const char *name, connman_bool_t
zero,
+ connman_netfilter_acct_get_cb_t cb,
+ void *user_data)
+{
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ struct nlmsghdr *nlmsg;
+ struct nlattr *attr;
+ uint16_t cmd;
+ int len, attrlen;
+ unsigned int id;
+
+ DBG("");
+
+ attrlen = strlen(name) + 1;
+ if (attrlen > NFACCT_NAME_MAX)
+ attrlen = NFACCT_NAME_MAX;
+
+ if (zero == FALSE)
+ cmd = NFNL_MSG_ACCT_GET;
+ else
+ cmd = NFNL_MSG_ACCT_GET_CTRZERO;
+
+ len = NFMSG_LEN(NLA_HDRLEN + attrlen);
+ nlmsg = create_nf_message(
+ (NFNL_SUBSYS_ACCT << 8) | cmd,
+ NLM_F_REQUEST | NLM_F_ACK, len);
+ if (nlmsg == NULL) {
+ g_free(cbd);
+ return 0;
+ }
+
+ attr = NFGEN_DATA(NLMSG_DATA(nlmsg));
+ append_attr_str(attr, NFACCT_NAME, attrlen, name);
+
+ id = queue_request(nlmsg, nfacct_get_cb, cbd);
+ if (id == 0) {
+ g_free(nlmsg);
+ g_free(cbd);
+ }
+
+ return id;
+}
+
+static int nfacct_del_cb(int error, uint16_t type, const void *data,
+ uint32_t len, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ connman_netfilter_acct_del_cb_t cb = cbd->cb;
+
+ DBG("type %d len %d", type, len);
+
+ cb(error, cbd->user_data);
+
+ g_free(cbd);
+
+ return 0;
+}
+
+unsigned int __connman_netfilter_acct_del(const char *name,
+ connman_netfilter_acct_del_cb_t cb,
+ void *user_data)
+{
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ struct nlmsghdr *nlmsg;
+ struct nlattr *attr;
+ int len, attrlen;
+ unsigned int id;
+
+ DBG("");
+
+ attrlen = strlen(name) + 1;
+ if (attrlen > NFACCT_NAME_MAX)
+ attrlen = NFACCT_NAME_MAX;
+
+ len = NFMSG_LEN(NLA_HDRLEN + attrlen);
+ nlmsg = create_nf_message(
+ (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_DEL,
+ NLM_F_REQUEST | NLM_F_ACK, len);
+ if (nlmsg == NULL) {
+ g_free(cbd);
+ return 0;
+ }
+
+ attr = NFGEN_DATA(NLMSG_DATA(nlmsg));
+ append_attr_str(attr, NFACCT_NAME, attrlen, name);
+
+ id = queue_request(nlmsg, nfacct_del_cb, cbd);
+ if (id == 0) {
+ g_free(nlmsg);
+ g_free(cbd);
+ }
+
+ return id;
+}
+
static void remove_request(struct cb_data *cbd)
{
struct nlmsghdr *hdr = cbd->data;
--
1.8.2.rc3.16.gce432ca
_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman