summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org>2005-08-06 09:00:20 +0000
committer/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org>2005-08-06 09:00:20 +0000
commit14c6542e9d79cc2ebc23c6a3fba691f63f55a26f (patch)
tree45242a1dfb590c5c418c361abf1e73bb1c22ed5d
parent80be16e6313bb810a50da22fad47b57aae9a6268 (diff)
add support for callback-based parsing of messages, similar to kernel API
-rw-r--r--include/libnfnetlink/libnfnetlink.h50
-rw-r--r--src/libnfnetlink.c120
2 files changed, 151 insertions, 19 deletions
diff --git a/include/libnfnetlink/libnfnetlink.h b/include/libnfnetlink/libnfnetlink.h
index 95ec004..d88c7fa 100644
--- a/include/libnfnetlink/libnfnetlink.h
+++ b/include/libnfnetlink/libnfnetlink.h
@@ -16,6 +16,12 @@
#define NFNL_BUFFSIZE 8192
+struct nfnl_callback {
+ int (*call)(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data);
+ void *data;
+ u_int16_t attr_count;
+};
+
struct nfnl_handle {
int fd;
struct sockaddr_nl local;
@@ -24,34 +30,51 @@ struct nfnl_handle {
u_int32_t seq;
u_int32_t dump;
struct nlmsghdr *last_nlhdr;
+
+ u_int8_t cb_count;
+ struct nfnl_callback *cb; /* array of callbacks */
};
/* get a new library handle */
-extern int nfnl_open(struct nfnl_handle *, u_int8_t, unsigned int);
+extern int nfnl_open(struct nfnl_handle *, u_int8_t, u_int8_t, unsigned int);
extern int nfnl_close(struct nfnl_handle *);
+
+/* sending of data */
extern int nfnl_send(struct nfnl_handle *, struct nlmsghdr *);
extern int nfnl_sendmsg(const struct nfnl_handle *, const struct msghdr *msg,
unsigned int flags);
extern int nfnl_sendiov(const struct nfnl_handle *nfnlh,
const struct iovec *iov, unsigned int num,
unsigned int flags);
-
extern void nfnl_fill_hdr(struct nfnl_handle *, struct nlmsghdr *,
unsigned int, u_int8_t, u_int16_t, u_int16_t,
u_int16_t);
+extern int nfnl_talk(struct nfnl_handle *, struct nlmsghdr *, pid_t,
+ unsigned, struct nlmsghdr *,
+ int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *);
-extern struct nfattr *nfnl_parse_hdr(const struct nfnl_handle *nfnlh,
- const struct nlmsghdr *nlh,
- struct nfgenmsg **genmsg);
-
+/* simple challenge/response */
extern int nfnl_listen(struct nfnl_handle *,
int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *),
void *);
-extern int nfnl_talk(struct nfnl_handle *, struct nlmsghdr *, pid_t,
- unsigned, struct nlmsghdr *,
- int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *),
- void *);
+/* receiving */
+extern int nfnl_callback_register(struct nfnl_handle *,
+ u_int8_t type, struct nfnl_callback *cb);
+extern int nfnl_callback_unregister(struct nfnl_handle *, u_int8_t type);
+extern int nfnl_handle_packet(struct nfnl_handle *, char *buf, int len);
+
+/* parsing */
+extern struct nfattr *nfnl_parse_hdr(const struct nfnl_handle *nfnlh,
+ const struct nlmsghdr *nlh,
+ struct nfgenmsg **genmsg);
+extern struct nlmsghdr *nfnl_get_msg_first(struct nfnl_handle *h,
+ const unsigned char *buf,
+ size_t len);
+extern struct nlmsghdr *nfnl_get_msg_next(struct nfnl_handle *h,
+ const unsigned char *buf,
+ size_t len);
/* nfnl attribute handling functions */
extern int nfnl_addattr_l(struct nlmsghdr *, int, int, void *, int);
@@ -74,12 +97,5 @@ extern void nfnl_build_nfa_iovec(struct iovec *iov, struct nfattr *nfa,
extern unsigned int nfnl_rcvbufsiz(struct nfnl_handle *h, unsigned int size);
-extern struct nlmsghdr *nfnl_get_msg_first(struct nfnl_handle *h,
- const unsigned char *buf,
- size_t len);
-extern struct nlmsghdr *nfnl_get_msg_next(struct nfnl_handle *h,
- const unsigned char *buf,
- size_t len);
-
extern void nfnl_dump_packet(struct nlmsghdr *, int, char *);
#endif /* __LIBNFNETLINK_H */
diff --git a/src/libnfnetlink.c b/src/libnfnetlink.c
index 56ef6c1..32eed10 100644
--- a/src/libnfnetlink.c
+++ b/src/libnfnetlink.c
@@ -62,11 +62,16 @@ void nfnl_dump_packet(struct nlmsghdr *nlh, int received_len, char *desc)
* subscriptions: netlink groups we want to be subscribed to
*
*/
-int nfnl_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
- u_int32_t subscriptions)
+int nfnl_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
+ u_int8_t cb_count, u_int32_t subscriptions)
{
int err;
unsigned int addr_len;
+ struct nfnl_callback *cb;
+
+ cb = malloc(sizeof(*cb) * cb_count);
+ if (!cb)
+ return -ENOMEM;
memset(nfnlh, 0, sizeof(*nfnlh));
nfnlh->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
@@ -101,6 +106,7 @@ int nfnl_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
}
nfnlh->seq = time(NULL);
nfnlh->subsys_id = subsys_id;
+ nfnlh->cb_count = cb_count;
return 0;
}
@@ -113,6 +119,7 @@ int nfnl_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
*/
int nfnl_close(struct nfnl_handle *nfnlh)
{
+ free(nfnlh->cb);
return close(nfnlh->fd);
}
@@ -625,3 +632,112 @@ struct nlmsghdr *nfnl_get_msg_next(struct nfnl_handle *h,
return nlh;
}
+
+int nfnl_callback_register(struct nfnl_handle *h,
+ u_int8_t type, struct nfnl_callback *cb)
+{
+ if (type >= h->cb_count)
+ return -EINVAL;
+
+ memcpy(&h->cb[type], cb, sizeof(*cb));
+
+ return 0;
+}
+
+int nfnl_callback_unregister(struct nfnl_handle *h, u_int8_t type)
+{
+ if (type >= h->cb_count)
+ return -EINVAL;
+
+ h->cb[type].call = NULL;
+
+ return 0;
+}
+
+static int nfnl_check_attributes(struct nfnl_handle *h, struct nlmsghdr *nlh,
+ struct nfattr *nfa[])
+{
+ int min_len;
+ u_int8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
+ struct nfnl_callback *cb = &h->cb[type];
+
+#if 1
+ /* checks need to be enabled as soon as this is called from
+ * somebody else than __nfnl_handle_msg */
+ if (type >= h->cb_count)
+ return -EINVAL;
+
+ min_len = NLMSG_ALIGN(sizeof(struct nfgenmsg));
+ if (nlh->nlmsg_len < min_len)
+ return -EINVAL;
+#endif
+ memset(nfa, 0, sizeof(struct nfattr *) * cb->attr_count);
+
+ if (nlh->nlmsg_len > min_len) {
+ struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+ while (NFA_OK(attr, attrlen)) {
+ unsigned int flavor = attr->nfa_type;
+ if (flavor) {
+ if (flavor > cb->attr_count)
+ return -EINVAL;
+ nfa[flavor - 1] = attr;
+ }
+ attr = NFA_NEXT(attr, attrlen);
+ }
+ }
+
+ return 0;
+}
+
+static int __nfnl_handle_msg(struct nfnl_handle *h, struct nlmsghdr *nlh,
+ int len)
+{
+ u_int8_t type;
+ int err = 0;
+ struct nfattr *nfa[h->cb_count];
+
+ if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != h->subsys_id)
+ return -1;
+
+ if (nlh->nlmsg_len < NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct nfgenmsg))))
+ return -1;
+
+ type = NFNL_MSG_TYPE(nlh->nlmsg_type);
+
+ if (type >= h->cb_count)
+ return -1;
+
+ if (h->cb[type].attr_count) {
+ err = nfnl_check_attributes(h, nlh, nfa);
+ if (err < 0)
+ return err;
+ if (h->cb[type].call)
+ return h->cb[type].call(nlh, nfa, h->cb[type].data);
+ }
+ return 0;
+}
+
+int nfnl_handle_packet(struct nfnl_handle *h, char *buf, int len)
+{
+
+ while (len >= NLMSG_SPACE(0)) {
+ u_int32_t rlen;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+ if (nlh->nlmsg_len < sizeof(struct nlmsghdr)
+ || len < nlh->nlmsg_len)
+ return -1;
+
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > len)
+ rlen = len;
+
+ if (__nfnl_handle_msg(h, nlh, rlen) < 0)
+ return -1;
+
+ len -= rlen;
+ }
+ return 0;
+}