From: Daniel Wagner <daniel.wag...@bmw-carit.de> --- src/connman.h | 32 +++++ src/netfilter.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 390 insertions(+)
diff --git a/src/connman.h b/src/connman.h index 937d6f2..1ffecf9 100644 --- a/src/connman.h +++ b/src/connman.h @@ -891,5 +891,37 @@ int __connman_nat_enable(const char *name, const char *address, void __connman_nat_disable(const char *name); +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.1.3.566.gaa39828 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman