mirror of https://github.com/sipwise/rtpengine.git
Change-Id: I3af7dcf0ed109db984c7eafca260eff89ec2b852pull/2035/head
parent
5586ed0ed6
commit
0ed79a2f0f
@ -0,0 +1,436 @@
|
||||
#include "netfilter_api.h"
|
||||
|
||||
//#include <asm/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netfilter/nf_tables_compat.h>
|
||||
#include <glib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
struct nfapi_socket {
|
||||
int fd;
|
||||
struct sockaddr_nl addr; // local
|
||||
};
|
||||
|
||||
struct nfapi_buf {
|
||||
GString *s; // buffer
|
||||
ssize_t last_hdr;
|
||||
GQueue nested;
|
||||
};
|
||||
|
||||
|
||||
static const struct sockaddr_nl zero_nl_sockaddr = { .nl_family = AF_NETLINK };
|
||||
|
||||
|
||||
nfapi_socket *nfapi_socket_open(void) {
|
||||
int fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
int ret = bind(fd, (struct sockaddr *) &zero_nl_sockaddr, sizeof(zero_nl_sockaddr));
|
||||
if (ret != 0)
|
||||
return NULL;
|
||||
|
||||
struct sockaddr_nl saddr;
|
||||
socklen_t slen = sizeof(saddr);
|
||||
ret = getsockname(fd, (struct sockaddr *) &saddr, &slen);
|
||||
if (slen < sizeof(saddr))
|
||||
return NULL;
|
||||
|
||||
nfapi_socket *s = g_new0(__typeof(*s), 1);
|
||||
s->fd = fd;
|
||||
s->addr = saddr;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void nfapi_socket_close(nfapi_socket *s) {
|
||||
if (s->fd != -1)
|
||||
close(s->fd);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
|
||||
nfapi_buf *nfapi_buf_new(void) {
|
||||
nfapi_buf *b = g_new0(__typeof(*b), 1);
|
||||
b->s = g_string_new("");
|
||||
b->last_hdr = -1;
|
||||
return b;
|
||||
}
|
||||
|
||||
void nfapi_buf_free(nfapi_buf *b) {
|
||||
g_string_free(b->s, TRUE);
|
||||
g_free(b);
|
||||
}
|
||||
|
||||
|
||||
static void *buf_add_store(GString *b, size_t s, ssize_t *store) {
|
||||
size_t cur = b->len;
|
||||
g_string_set_size(b, cur + s);
|
||||
if (store)
|
||||
*store = cur;
|
||||
return b->str + cur;
|
||||
}
|
||||
|
||||
static struct nlmsghdr *hdr_add(nfapi_buf *b, size_t s) {
|
||||
return buf_add_store(b->s, s, &b->last_hdr);
|
||||
}
|
||||
|
||||
static struct nlmsghdr *hdr_get_last(nfapi_buf *b) {
|
||||
assert(b->last_hdr != -1);
|
||||
return (struct nlmsghdr *) (b->s->str + b->last_hdr);
|
||||
}
|
||||
|
||||
static void *item_add(nfapi_buf *b, size_t s) {
|
||||
void *ret = buf_add_store(b->s, s, NULL);
|
||||
__auto_type hdr = hdr_get_last(b);
|
||||
hdr->nlmsg_len += s;
|
||||
for (__auto_type l = b->nested.head; l; l = l->next) {
|
||||
size_t off = (size_t) l->data;
|
||||
struct nlattr *attr = (struct nlattr *) (b->s->str + off);
|
||||
attr->nla_len += s;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void add_msg(nfapi_buf *b, uint16_t type, uint16_t family, uint16_t flags, uint16_t seq, uint16_t res_id) {
|
||||
struct nlmsghdr *hdr = hdr_add(b, sizeof(*hdr));
|
||||
*hdr = (__typeof(*hdr)) {
|
||||
.nlmsg_type = type,
|
||||
.nlmsg_flags = flags,
|
||||
.nlmsg_seq = seq,
|
||||
.nlmsg_pid = 0,
|
||||
.nlmsg_len = sizeof(*hdr),
|
||||
};
|
||||
struct nfgenmsg *fam = item_add(b, sizeof(*fam));
|
||||
*fam = (__typeof(*fam)) {
|
||||
.nfgen_family = family,
|
||||
.version = NFNETLINK_V0,
|
||||
.res_id = htons(res_id),
|
||||
};
|
||||
}
|
||||
|
||||
void nfapi_add_msg(nfapi_buf *b, uint16_t type, uint16_t family, uint16_t flags) {
|
||||
return add_msg(b, (NFNL_SUBSYS_NFTABLES << 8) | type, family, flags, 0, 0);
|
||||
}
|
||||
|
||||
void nfapi_batch_begin(nfapi_buf *b) {
|
||||
add_msg(b, NFNL_MSG_BATCH_BEGIN, AF_UNSPEC, NLM_F_REQUEST, 0, NFNL_SUBSYS_NFTABLES);
|
||||
}
|
||||
void nfapi_batch_end(nfapi_buf *b) {
|
||||
add_msg(b, NFNL_MSG_BATCH_END, AF_UNSPEC, NLM_F_REQUEST, 0, NFNL_SUBSYS_NFTABLES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void nfapi_add_attr(nfapi_buf *b, uint16_t type, const void *data, size_t len) {
|
||||
struct nlattr *attr = item_add(b, sizeof(*attr));
|
||||
*attr = (__typeof(*attr)) {
|
||||
.nla_type = type,
|
||||
.nla_len = sizeof(*attr) + len,
|
||||
};
|
||||
void *d = item_add(b, NFA_ALIGN(len));
|
||||
memset(d, 0, NFA_ALIGN(len));
|
||||
memcpy(d, data, len);
|
||||
}
|
||||
|
||||
void nfapi_add_str_attr(nfapi_buf *b, uint16_t type, const char *s) {
|
||||
nfapi_add_attr(b, type, s, strlen(s) + 1);
|
||||
}
|
||||
void nfapi_add_u32_attr(nfapi_buf *b, uint16_t type, uint32_t u) {
|
||||
nfapi_add_attr(b, type, &u, sizeof(u));
|
||||
}
|
||||
void nfapi_add_u64_attr(nfapi_buf *b, uint16_t type, uint64_t u) {
|
||||
nfapi_add_attr(b, type, &u, sizeof(u));
|
||||
}
|
||||
|
||||
void nfapi_nested_begin(nfapi_buf *b, uint16_t type) {
|
||||
g_queue_push_tail(&b->nested, (void *) b->s->len);
|
||||
nfapi_add_attr(b, type | NLA_F_NESTED, NULL, 0);
|
||||
}
|
||||
|
||||
void nfapi_nested_end(nfapi_buf *b) {
|
||||
assert(b->nested.length != 0);
|
||||
g_queue_pop_tail(&b->nested);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool nfapi_send_buf(nfapi_socket *s, nfapi_buf *b) {
|
||||
ssize_t ret = sendto(s->fd, b->s->str, b->s->len, 0, (struct sockaddr *) &zero_nl_sockaddr,
|
||||
sizeof(zero_nl_sockaddr));
|
||||
if (ret != b->s->len)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *nfapi_recv_iter(nfapi_socket *s, const nfapi_callbacks *c, void *userdata) {
|
||||
while (true) {
|
||||
int8_t buf[8192];
|
||||
|
||||
union {
|
||||
struct sockaddr_storage sst;
|
||||
struct sockaddr_nl ssn;
|
||||
} ss;
|
||||
socklen_t ssl = sizeof(ss);
|
||||
errno = 0;
|
||||
ssize_t r = recvfrom(s->fd, buf, sizeof(buf), 0, (struct sockaddr *) &ss.sst, &ssl);
|
||||
|
||||
if (r < 0 || r > sizeof(buf)
|
||||
|| ssl < sizeof(ss.ssn)
|
||||
|| ss.ssn.nl_family != AF_NETLINK
|
||||
|| ss.ssn.nl_pid != 0)
|
||||
return "error while receiving from netlink socket";
|
||||
|
||||
if (r == 0)
|
||||
return NULL;
|
||||
|
||||
size_t off = 0;
|
||||
while (off < r) {
|
||||
const struct nlmsghdr *hdr;
|
||||
if (off + sizeof(hdr) > r)
|
||||
return "message too short for header";
|
||||
|
||||
hdr = (struct nlmsghdr *) (buf + off);
|
||||
uint16_t subsys = NFNL_SUBSYS_ID(hdr->nlmsg_type);
|
||||
uint16_t type = NFNL_MSG_TYPE(hdr->nlmsg_type);
|
||||
|
||||
if (hdr->nlmsg_len == 0)
|
||||
return "zero length message";
|
||||
|
||||
size_t next = off + sizeof(*hdr);
|
||||
|
||||
off += hdr->nlmsg_len;
|
||||
|
||||
assert(hdr->nlmsg_pid == s->addr.nl_pid);
|
||||
|
||||
if (subsys == NFNL_SUBSYS_NFTABLES) {
|
||||
struct nfgenmsg *fam;
|
||||
if (next + sizeof(*fam) > r)
|
||||
return "message too short for genmsg";
|
||||
|
||||
fam = (struct nfgenmsg *) (buf + next);
|
||||
next += sizeof(*fam);
|
||||
|
||||
if (next > off)
|
||||
return "message too short after genmsg";
|
||||
|
||||
if (fam->version != NFNETLINK_V0)
|
||||
return "netlink version not v0";
|
||||
|
||||
switch (type) {
|
||||
case NFT_MSG_NEWRULE:
|
||||
if (c && c->rule)
|
||||
c->rule(buf + next, off - next, userdata);
|
||||
break;
|
||||
|
||||
case NFT_MSG_NEWCHAIN:
|
||||
if (c && c->chain)
|
||||
c->chain(buf + next, off - next, userdata);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
};
|
||||
}
|
||||
else {
|
||||
if (type == NLMSG_DONE)
|
||||
return NULL;
|
||||
|
||||
if (type == NLMSG_ERROR) {
|
||||
struct nlmsgerr *err;
|
||||
errno = ERANGE;
|
||||
if (next + sizeof(*err) > r)
|
||||
return "error but also message too short";
|
||||
|
||||
err = (struct nlmsgerr *) (buf + next);
|
||||
|
||||
if (err->error == 0)
|
||||
return NULL;
|
||||
|
||||
errno = -err->error;
|
||||
return "error returned from netlink, see errno";
|
||||
}
|
||||
else
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define foreach_nlattr(l, attr, type, data, data_len, fail_ret) \
|
||||
size_t __off = 0; \
|
||||
\
|
||||
while (__off < l) { \
|
||||
const struct nlattr *attr; \
|
||||
errno = EMSGSIZE; \
|
||||
if (__off + sizeof(*attr) > l) \
|
||||
return fail_ret; \
|
||||
\
|
||||
attr = (struct nlattr *) (buf + __off); \
|
||||
errno = ERANGE; \
|
||||
if (attr->nla_len == 0 || __off + attr->nla_len > l) \
|
||||
return fail_ret; \
|
||||
\
|
||||
uint16_t type = attr->nla_type & NLA_TYPE_MASK; \
|
||||
\
|
||||
const int8_t *data = buf + __off + sizeof(*attr); \
|
||||
size_t data_len __attribute__((unused)) = attr->nla_len - sizeof(*attr); \
|
||||
\
|
||||
__off += NFA_ALIGN(attr->nla_len); \
|
||||
|
||||
|
||||
static bool nested_expr_iter(const int8_t *buf, size_t l,
|
||||
const char **name, const int8_t **expr_data, size_t *expr_len)
|
||||
{
|
||||
foreach_nlattr(l, attr, type, data, data_len, false)
|
||||
switch (type) {
|
||||
case NFTA_EXPR_NAME:
|
||||
*name = (char *) data;
|
||||
break;
|
||||
case NFTA_EXPR_DATA:
|
||||
*expr_data = data;
|
||||
*expr_len = data_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nested_verdict_iter(const int8_t *buf, size_t l, int32_t *code, const char **chain) {
|
||||
foreach_nlattr(l, attr, type, data, data_len, false)
|
||||
switch (type) {
|
||||
case NFTA_VERDICT_CODE:
|
||||
if (data_len != sizeof(int32_t))
|
||||
return false;
|
||||
*code = ntohl(*(int32_t *) data);
|
||||
break;
|
||||
|
||||
case NFTA_VERDICT_CHAIN:
|
||||
*chain = (char *) data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nested_immediate_iter(const int8_t *buf, size_t l, int32_t *code, const char **chain) {
|
||||
foreach_nlattr(l, attr, type, data, data_len, false)
|
||||
if (type == NFTA_DATA_VERDICT) {
|
||||
if (!nested_verdict_iter(data, data_len, code, chain))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *expr_iter(const int8_t *buf, size_t l, const nfapi_callbacks *c, void *userdata) {
|
||||
foreach_nlattr(l, attr, type, data, data_len, "error in expression message format")
|
||||
if (type != NFTA_LIST_ELEM)
|
||||
return "not a list element";
|
||||
|
||||
const char *name = NULL;
|
||||
const int8_t *expr_data = NULL;
|
||||
size_t expr_len = 0;
|
||||
|
||||
if (!nested_expr_iter(data, data_len, &name, &expr_data, &expr_len))
|
||||
return "error in expression items";
|
||||
|
||||
if (!name)
|
||||
return "expression has no name";
|
||||
|
||||
if (c && c->expression) {
|
||||
const char *err = c->expression(name, expr_data, expr_len, userdata);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *nfapi_rule_iter(const int8_t *buf, size_t l, const nfapi_callbacks *c, void *userdata) {
|
||||
//const char *table = NULL;
|
||||
//const char *chain = NULL;
|
||||
int64_t handle = -1;
|
||||
|
||||
foreach_nlattr(l, attr, type, data, data_len, "error in rule message format")
|
||||
switch (type) {
|
||||
case NFTA_RULE_TABLE:
|
||||
//table = data;
|
||||
//printf("table %s\n", data);
|
||||
break;
|
||||
case NFTA_RULE_CHAIN:
|
||||
//chain = data;
|
||||
//printf("chain %s\n", data);
|
||||
break;
|
||||
case NFTA_RULE_HANDLE:
|
||||
if (data_len != sizeof(handle))
|
||||
return "handle size incorrect";
|
||||
handle = *(int64_t *) data;
|
||||
if (c && c->handle)
|
||||
c->handle(handle, userdata);
|
||||
break;
|
||||
case NFTA_RULE_EXPRESSIONS:;
|
||||
const char *err = expr_iter(data, data_len, c, userdata);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *nfapi_get_immediate_chain(const int8_t *buf, size_t l) {
|
||||
const char *chain = NULL;
|
||||
int32_t verdict_code = 0;
|
||||
|
||||
foreach_nlattr(l, attr, type, data, data_len, NULL)
|
||||
if (type == NFTA_IMMEDIATE_DATA && data_len >= sizeof(struct nlattr)) {
|
||||
if (!nested_immediate_iter(data, data_len, &verdict_code, &chain))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((verdict_code == NFT_JUMP || verdict_code == NFT_GOTO) && chain)
|
||||
return chain;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *nfapi_get_target(const int8_t *buf, size_t l, void *info, size_t *info_len) {
|
||||
const char *tg = NULL;
|
||||
|
||||
size_t buf_len = 0;
|
||||
if (info_len && info) {
|
||||
buf_len = *info_len;
|
||||
*info_len = 0;
|
||||
}
|
||||
|
||||
foreach_nlattr(l, attr, type, data, data_len, NULL)
|
||||
switch (type) {
|
||||
case NFTA_TARGET_NAME:
|
||||
tg = (char *) data;
|
||||
break;
|
||||
|
||||
case NFTA_TARGET_INFO:
|
||||
if (!buf_len)
|
||||
break;
|
||||
buf_len = MIN(buf_len, data_len);
|
||||
memcpy(info, data, buf_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tg;
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
#ifndef _NETFILTER_API_H
|
||||
#define _NETFILTER_API_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct nfapi_socket nfapi_socket;
|
||||
typedef struct nfapi_buf nfapi_buf;
|
||||
|
||||
typedef struct {
|
||||
const char *(*rule)(const int8_t *, size_t, void *userdata);
|
||||
const char *(*chain)(const int8_t *, size_t, void *userdata);
|
||||
const char *(*expression)(const char *, const int8_t *, size_t, void *userdata);
|
||||
void (*handle)(int64_t, void *userdata);
|
||||
} nfapi_callbacks;
|
||||
|
||||
|
||||
nfapi_socket *nfapi_socket_open(void);
|
||||
void nfapi_socket_close(nfapi_socket *);
|
||||
|
||||
|
||||
nfapi_buf *nfapi_buf_new(void);
|
||||
void nfapi_buf_free(nfapi_buf *);
|
||||
|
||||
void nfapi_add_msg(nfapi_buf *, uint16_t type, uint16_t family, uint16_t flags);
|
||||
|
||||
void nfapi_add_attr(nfapi_buf *b, uint16_t type, const void *data, size_t len);
|
||||
void nfapi_add_str_attr(nfapi_buf *b, uint16_t type, const char *s);
|
||||
void nfapi_add_u32_attr(nfapi_buf *b, uint16_t type, uint32_t u);
|
||||
void nfapi_add_u64_attr(nfapi_buf *b, uint16_t type, uint64_t u);
|
||||
|
||||
void nfapi_nested_begin(nfapi_buf *, uint16_t type);
|
||||
void nfapi_nested_end(nfapi_buf *);
|
||||
|
||||
void nfapi_batch_begin(nfapi_buf *);
|
||||
void nfapi_batch_end(nfapi_buf *);
|
||||
|
||||
|
||||
bool nfapi_send_buf(nfapi_socket *, nfapi_buf *);
|
||||
|
||||
const char *nfapi_recv_iter(nfapi_socket *, const nfapi_callbacks *, void *userdata);
|
||||
|
||||
const char *nfapi_rule_iter(const int8_t *, size_t, const nfapi_callbacks *, void *userdata);
|
||||
|
||||
const char *nfapi_get_immediate_chain(const int8_t *, size_t);
|
||||
const char *nfapi_get_target(const int8_t *, size_t, void *info, size_t *info_len);
|
||||
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(nfapi_socket, nfapi_socket_close);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(nfapi_buf, nfapi_buf_free);
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue