From 14c6542e9d79cc2ebc23c6a3fba691f63f55a26f Mon Sep 17 00:00:00 2001 From: "/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org" Date: Sat, 6 Aug 2005 09:00:20 +0000 Subject: add support for callback-based parsing of messages, similar to kernel API --- include/libnfnetlink/libnfnetlink.h | 50 ++++++++++----- src/libnfnetlink.c | 120 +++++++++++++++++++++++++++++++++++- 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; +} -- cgit v1.2.3