/** * nfnl_talk - send a request and then receive and process messages returned * @nfnlh: nfnetelink handler * @n: netlink message that contains the request * @peer: peer PID * @groups: netlink groups * @junk: callback called if out-of-sequence messages were received * @jarg: data for the junk callback * * This function is used to request an action that does not returns any * information. On error, a negative value is returned, errno could be * set appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_query() instead. */ int nfnl_talk(struct nfnl_handle *nfnlh, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer, int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { char buf[NFNL_BUFFSIZE]; struct sockaddr_nl nladdr; struct nlmsghdr *h; unsigned int seq; int status; struct iovec iov = { (void *)n, n->nlmsg_len }; struct msghdr msg = { (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = peer; nladdr.nl_groups = groups; n->nlmsg_seq = seq = ++nfnlh->seq; /* FIXME: why ? */ if (!answer) n->nlmsg_flags |= NLM_F_ACK; status = sendmsg(nfnlh->fd, &msg, 0); if (status < 0) { nfnl_error("sendmsg(netlink) %s", strerror(errno)); return -1; } iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { status = recvmsg(nfnlh->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; nfnl_error("recvmsg over-run"); continue; } if (status == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len %d", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); int err; if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Truncated message\n"); return -1; } nfnl_error("Malformed message: len=%d\n", len); return -1; /* FIXME: libnetlink exits here */ } if (h->nlmsg_pid != nfnlh->local.nl_pid || h->nlmsg_seq != seq) { if (junk) { err = junk(&nladdr, h, jarg); if (err < 0) return err; } goto cont; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(h); if (l < sizeof(struct nlmsgerr)) nfnl_error("ERROR truncated\n"); else { errno = -err->error; if (errno == 0) { if (answer) memcpy(answer, h, h->nlmsg_len); return 0; } perror("NFNETLINK answers"); } return err->error; } if (answer) { memcpy(answer, h, h->nlmsg_len); return 0; } nfnl_error("Unexpected reply!\n"); cont: status -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Messages truncated\n"); continue; } if (status) { nfnl_error("Remnant of size %d\n", status); exit(1); } } }
/** * nfnl_listen: listen for one or more netlink messages * @nfnhl: libnfnetlink handle * @handler: callback function to be called for every netlink message * - the callback handler should normally return 0 * - but may return a negative error code which will cause * nfnl_listen to return immediately with the same error code * - or return a postivie error code which will cause * nfnl_listen to return after it has finished processing all * the netlink messages in the current packet * Thus a positive error code will terminate nfnl_listen "soon" * without any loss of data, a negative error code will terminate * nfnl_listen "very soon" and throw away data already read from * the netlink socket. * @jarg: opaque argument passed on to callback * * This function is used to receive and process messages coming from an open * nfnetlink handler like events or information request via nfnl_send(). * * On error, -1 is returned, unfortunately errno is not always set * appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_receive_process() instead. */ int nfnl_listen(struct nfnl_handle *nfnlh, int (*handler)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { struct sockaddr_nl nladdr; char buf[NFNL_BUFFSIZE] __attribute__ ((aligned)); struct iovec iov; int remain; struct nlmsghdr *h; struct nlmsgerr *msgerr; int quit=0; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; iov.iov_base = buf; iov.iov_len = sizeof(buf); while (! quit) { remain = recvmsg(nfnlh->fd, &msg, 0); if (remain < 0) { if (errno == EINTR) continue; /* Bad file descriptor */ else if (errno == EBADF) break; else if (errno == EAGAIN) break; nfnl_error("recvmsg overrun: %s", strerror(errno)); continue; } if (remain == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len (%d)", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; remain >= sizeof(*h);) { int err; int len = h->nlmsg_len; int l = len - sizeof(*h); if (l < 0 || len > remain) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); return -1; } nfnl_error("Malformed msg (len=%d)", len); return -1; } /* end of messages reached, let's return */ if (h->nlmsg_type == NLMSG_DONE) return 0; /* Break the loop if success is explicitely * reported via NLM_F_ACK flag set */ if (h->nlmsg_type == NLMSG_ERROR) { msgerr = NLMSG_DATA(h); return msgerr->error; } err = handler(&nladdr, h, jarg); if (err < 0) return err; quit |= err; /* FIXME: why not _NEXT macros, etc.? */ //h = NLMSG_NEXT(h, remain); remain -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); continue; } if (remain) { nfnl_error("remnant size %d", remain); return -1; } } return quit; } /** * nfnl_talk - send a request and then receive and process messages returned * @nfnlh: nfnetelink handler * @n: netlink message that contains the request * @peer: peer PID * @groups: netlink groups * @junk: callback called if out-of-sequence messages were received * @jarg: data for the junk callback * * This function is used to request an action that does not returns any * information. On error, a negative value is returned, errno could be * set appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_query() instead. */ int nfnl_talk(struct nfnl_handle *nfnlh, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer, int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { char buf[NFNL_BUFFSIZE] __attribute__ ((aligned)); struct sockaddr_nl nladdr; struct nlmsghdr *h; unsigned int seq; int status; struct iovec iov = { n, n->nlmsg_len }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = peer; nladdr.nl_groups = groups; n->nlmsg_seq = seq = ++nfnlh->seq; /* FIXME: why ? */ if (!answer) n->nlmsg_flags |= NLM_F_ACK; status = sendmsg(nfnlh->fd, &msg, 0); if (status < 0) { nfnl_error("sendmsg(netlink) %s", strerror(errno)); return -1; } iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { status = recvmsg(nfnlh->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; nfnl_error("recvmsg over-run"); continue; } if (status == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len %d", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); int err; if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Truncated message\n"); return -1; } nfnl_error("Malformed message: len=%d\n", len); return -1; /* FIXME: libnetlink exits here */ } if (h->nlmsg_pid != nfnlh->local.nl_pid || h->nlmsg_seq != seq) { if (junk) { err = junk(&nladdr, h, jarg); if (err < 0) return err; } goto cont; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(h); if (l < sizeof(struct nlmsgerr)) nfnl_error("ERROR truncated\n"); else { errno = -err->error; if (errno == 0) { if (answer) memcpy(answer, h, h->nlmsg_len); return 0; } perror("NFNETLINK answers"); } return err->error; } if (answer) { memcpy(answer, h, h->nlmsg_len); return 0; } nfnl_error("Unexpected reply!\n"); cont: status -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Messages truncated\n"); continue; } if (status) nfnl_error("Remnant of size %d\n", status); } } /** * nfnl_addattr_l - Add variable length attribute to nlmsghdr * @n: netlink message header to which attribute is to be added * @maxlen: maximum length of netlink message header * @type: type of new attribute * @data: content of new attribute * @len: attribute length */ int nfnl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = NFA_LENGTH(alen); struct nfattr *nfa; assert(n); assert(maxlen > 0); assert(type >= 0); if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { errno = ENOSPC; return -1; } nfa = NLMSG_TAIL(n); nfa->nfa_type = type; nfa->nfa_len = len; memcpy(NFA_DATA(nfa), data, alen); memset((uint8_t *)nfa + nfa->nfa_len, 0, NFA_ALIGN(alen) - alen); n->nlmsg_len = (NLMSG_ALIGN(n->nlmsg_len) + NFA_ALIGN(len)); return 0; }
/** * nfnl_listen: listen for one or more netlink messages * @nfnhl: libnfnetlink handle * @handler: callback function to be called for every netlink message * - the callback handler should normally return 0 * - but may return a negative error code which will cause * nfnl_listen to return immediately with the same error code * - or return a postivie error code which will cause * nfnl_listen to return after it has finished processing all * the netlink messages in the current packet * Thus a positive error code will terminate nfnl_listen "soon" * without any loss of data, a negative error code will terminate * nfnl_listen "very soon" and throw away data already read from * the netlink socket. * @jarg: opaque argument passed on to callback * * This function is used to receive and process messages coming from an open * nfnetlink handler like events or information request via nfnl_send(). * * On error, -1 is returned, unfortunately errno is not always set * appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_receive_process() instead. */ int nfnl_listen(struct nfnl_handle *nfnlh, int (*handler)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { struct sockaddr_nl nladdr; char buf[NFNL_BUFFSIZE]; struct iovec iov; int remain; struct nlmsghdr *h; struct nlmsgerr *msgerr; int quit=0; struct msghdr msg = { (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; iov.iov_base = buf; iov.iov_len = sizeof(buf); while (! quit) { remain = recvmsg(nfnlh->fd, &msg, 0); if (remain < 0) { if (errno == EINTR) continue; /* Bad file descriptor */ else if (errno == EBADF) break; else if (errno == EAGAIN) break; nfnl_error("recvmsg overrun: %s", strerror(errno)); continue; } if (remain == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len (%d)", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; remain >= sizeof(*h);) { int err; int len = h->nlmsg_len; int l = len - sizeof(*h); if (l < 0 || len > remain) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); return -1; } nfnl_error("Malformed msg (len=%d)", len); return -1; } /* end of messages reached, let's return */ if (h->nlmsg_type == NLMSG_DONE) return 0; /* Break the loop if success is explicitely * reported via NLM_F_ACK flag set */ if (h->nlmsg_type == NLMSG_ERROR) { msgerr = NLMSG_DATA(h); return msgerr->error; } err = handler(&nladdr, h, jarg); if (err < 0) return err; quit |= err; /* FIXME: why not _NEXT macros, etc.? */ //h = NLMSG_NEXT(h, remain); remain -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); continue; } if (remain) { nfnl_error("remnant size %d", remain); return -1; } } return quit; }