| /* | 
 |  * Copyright (C) 2011 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *	http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | /* NOTICE: This is a clean room re-implementation of libnl */ | 
 |  | 
 | #include <errno.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <fcntl.h> | 
 | #include <sys/socket.h> | 
 | #include "netlink-types.h" | 
 |  | 
 | #define NL_BUFFER_SZ (32768U) | 
 |  | 
 | /* Checks message for completeness and sends it out */ | 
 | int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) | 
 | { | 
 | 	struct nlmsghdr *nlh = msg->nm_nlh; | 
 | 	struct timeval tv; | 
 |  | 
 | 	if (!nlh) { | 
 | 		int errsv = errno; | 
 | 		fprintf(stderr, "Netlink message header is NULL!\n"); | 
 | 		return -errsv; | 
 | 	} | 
 |  | 
 | 	/* Complete the nl_msg header */ | 
 | 	if (gettimeofday(&tv, NULL)) | 
 | 		nlh->nlmsg_seq = 1; | 
 | 	else | 
 | 		nlh->nlmsg_seq = (int) tv.tv_sec; | 
 | 	nlh->nlmsg_pid = sk->s_local.nl_pid; | 
 | 	nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK; | 
 |  | 
 | 	return nl_send(sk, msg); | 
 | } | 
 |  | 
 | /* Receives a netlink message, allocates a buffer in *buf and stores | 
 |  * the message content. The peer's netlink address is stored in | 
 |  * *nla. The caller is responsible for freeing the buffer allocated in | 
 |  * *buf if a positive value is returned. Interrupted system calls are | 
 |  * handled by repeating the read. The input buffer size is determined | 
 |  * by peeking before the actual read is done */ | 
 | int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \ | 
 | 	unsigned char **buf, struct ucred **creds) | 
 | { | 
 | 	int rc = -1; | 
 | 	int sk_flags; | 
 | 	int RECV_BUF_SIZE = getpagesize(); | 
 | 	int errsv; | 
 | 	struct iovec recvmsg_iov; | 
 | 	struct msghdr msg; | 
 |  | 
 | 	/* Allocate buffer */ | 
 | 	*buf = (unsigned char *) malloc(RECV_BUF_SIZE); | 
 | 	if (!(*buf)) { | 
 | 		rc = -ENOMEM; | 
 | 		goto fail; | 
 | 	} | 
 |  | 
 | 	/* Prepare to receive message */ | 
 | 	recvmsg_iov.iov_base = *buf; | 
 | 	recvmsg_iov.iov_len = RECV_BUF_SIZE; | 
 |  | 
 | 	msg.msg_name = (void *) &sk->s_peer; | 
 | 	msg.msg_namelen = sizeof(sk->s_peer); | 
 | 	msg.msg_iov = &recvmsg_iov; | 
 | 	msg.msg_iovlen = 1; | 
 | 	msg.msg_control = NULL; | 
 | 	msg.msg_controllen = 0; | 
 | 	msg.msg_flags = 0; | 
 |  | 
 | 	/* Make non blocking and then restore previous setting */ | 
 | 	sk_flags = fcntl(sk->s_fd, F_GETFL, 0); | 
 | 	fcntl(sk->s_fd, F_SETFL, O_NONBLOCK); | 
 | 	rc = recvmsg(sk->s_fd, &msg, 0); | 
 | 	errsv = errno; | 
 | 	fcntl(sk->s_fd, F_SETFL, sk_flags); | 
 |  | 
 | 	if (rc < 0) { | 
 | 		rc = -errsv; | 
 | 		free(*buf); | 
 | 		*buf = NULL; | 
 | 	} | 
 |  | 
 | fail: | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* Receive a set of messages from a netlink socket */ | 
 | /* NOTE: Does not currently support callback replacements!!! */ | 
 | int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) | 
 | { | 
 | 	struct sockaddr_nl nla; | 
 | 	struct ucred *creds; | 
 |  | 
 | 	int rc, cb_rc = NL_OK, done = 0; | 
 |  | 
 | 	do { | 
 | 		unsigned char *buf; | 
 | 		int i, rem, flags; | 
 | 		struct nlmsghdr *nlh; | 
 | 		struct nlmsgerr *nlme; | 
 | 		struct nl_msg *msg; | 
 |  | 
 | 		done = 0; | 
 | 		rc = nl_recv(sk, &nla, &buf, &creds); | 
 | 		if (rc < 0) | 
 | 			break; | 
 |  | 
 | 		nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) { | 
 |  | 
 | 			if (rc <= 0 || cb_rc == NL_STOP) | 
 | 				break; | 
 |  | 
 | 			/* Check for callbacks */ | 
 |  | 
 | 			msg = (struct nl_msg *) malloc(sizeof(struct nl_msg)); | 
 | 			memset(msg, 0, sizeof(*msg)); | 
 | 			msg->nm_nlh = nlh; | 
 |  | 
 | 			/* Check netlink message type */ | 
 |  | 
 | 			switch (msg->nm_nlh->nlmsg_type) { | 
 | 			case NLMSG_ERROR:	  /* Used for ACK too */ | 
 | 				/* Certainly we should be doing some | 
 | 				 * checking here to make sure this | 
 | 				 * message is intended for us */ | 
 | 				nlme = nlmsg_data(msg->nm_nlh); | 
 | 				if (nlme->error == 0) | 
 | 					msg->nm_nlh->nlmsg_flags |= NLM_F_ACK; | 
 |  | 
 | 				rc = nlme->error; | 
 | 				cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg); | 
 | 				nlme = NULL; | 
 | 				break; | 
 |  | 
 | 			case NLMSG_DONE: | 
 | 				done = 1; | 
 |  | 
 | 			case NLMSG_OVERRUN: | 
 | 			case NLMSG_NOOP: | 
 | 			default: | 
 | 				break; | 
 | 			}; | 
 |  | 
 | 			for (i = 0; i <= NL_CB_TYPE_MAX; i++) { | 
 |  | 
 | 				if (cb->cb_set[i]) { | 
 | 					switch (i) { | 
 | 					case NL_CB_VALID: | 
 | 						if (rc > 0) | 
 | 							cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); | 
 | 						break; | 
 |  | 
 | 					case NL_CB_FINISH: | 
 | 						if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) && | 
 | 							(msg->nm_nlh->nlmsg_type & NLMSG_DONE)) | 
 | 							cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); | 
 |  | 
 | 						break; | 
 |  | 
 | 					case NL_CB_ACK: | 
 | 						if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK) | 
 | 							cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); | 
 |  | 
 | 						break; | 
 | 					default: | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			free(msg); | 
 | 			if (done) | 
 | 				break; | 
 | 		} | 
 | 		free(buf); | 
 | 		buf = NULL; | 
 |  | 
 | 		if (done) | 
 | 			break; | 
 | 	} while (rc > 0 && cb_rc != NL_STOP); | 
 |  | 
 | success: | 
 | fail: | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* Send raw data over netlink socket */ | 
 | int nl_send(struct nl_sock *sk, struct nl_msg *msg) | 
 | { | 
 | 	struct nlmsghdr *nlh = nlmsg_hdr(msg); | 
 | 	struct iovec msg_iov; | 
 |  | 
 | 	/* Create IO vector with Netlink message */ | 
 | 	msg_iov.iov_base = nlh; | 
 | 	msg_iov.iov_len = nlh->nlmsg_len; | 
 |  | 
 | 	return nl_send_iovec(sk, msg, &msg_iov, 1); | 
 | } | 
 |  | 
 | /* Send netlink message */ | 
 | int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, | 
 | 		   struct iovec *iov, unsigned iovlen) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	/* Socket message */ | 
 | 	struct msghdr mh = { | 
 | 		.msg_name = (void *) &sk->s_peer, | 
 | 		.msg_namelen = sizeof(sk->s_peer), | 
 | 		.msg_iov = iov, | 
 | 		.msg_iovlen = iovlen, | 
 | 		.msg_control = NULL, | 
 | 		.msg_controllen = 0, | 
 | 		.msg_flags = 0 | 
 | 	}; | 
 |  | 
 | 	/* Send message and verify sent */ | 
 | 	rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0); | 
 | 	if (rc < 0) | 
 | 		fprintf(stderr, "Error sending netlink message: %d\n", errno); | 
 | 	return rc; | 
 |  | 
 | } | 
 |  | 
 | /* Send netlink message with control over sendmsg() message header */ | 
 | int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) | 
 | { | 
 | 	return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr); | 
 | } | 
 |  | 
 | /* Create and connect netlink socket */ | 
 | int nl_connect(struct nl_sock *sk, int protocol) | 
 | { | 
 | 	struct sockaddr addr; | 
 | 	socklen_t addrlen; | 
 | 	int rc; | 
 |  | 
 | 	/* Create RX socket */ | 
 | 	sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol); | 
 | 	if (sk->s_fd < 0) | 
 | 		return -errno; | 
 |  | 
 | 	/* Set size of RX and TX buffers */ | 
 | 	if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0) | 
 | 		return -errno; | 
 |  | 
 | 	/* Bind RX socket */ | 
 | 	rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \ | 
 | 		sizeof(sk->s_local)); | 
 | 	if (rc < 0) | 
 | 		return -errno; | 
 | 	addrlen = sizeof(addr); | 
 | 	getsockname(sk->s_fd, &addr, &addrlen); | 
 |  | 
 | 	return 0; | 
 |  | 
 | } |