 05742faf25
			
		
	
	
	05742faf25
	
	
	
		
			
			Use the precalculated size instead of obfuscating the message length calculation by first subtracting the netlink header length from size and then use the NLMSG_LENGTH() macro to add it back again. Signed-off-by: Mathias Krause <minipli@googlemail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
		
			
				
	
	
		
			250 lines
		
	
	
	
		
			5.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
	
		
			5.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 	ucon.c
 | |
|  *
 | |
|  * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
 | |
|  *
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | |
|  */
 | |
| 
 | |
| #include <asm/types.h>
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <sys/poll.h>
 | |
| 
 | |
| #include <linux/netlink.h>
 | |
| #include <linux/rtnetlink.h>
 | |
| 
 | |
| #include <arpa/inet.h>
 | |
| 
 | |
| #include <stdbool.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <time.h>
 | |
| #include <getopt.h>
 | |
| 
 | |
| #include <linux/connector.h>
 | |
| 
 | |
| #define DEBUG
 | |
| #define NETLINK_CONNECTOR 	11
 | |
| 
 | |
| /* Hopefully your userspace connector.h matches this kernel */
 | |
| #define CN_TEST_IDX		CN_NETLINK_USERS + 3
 | |
| #define CN_TEST_VAL		0x456
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #define ulog(f, a...) fprintf(stdout, f, ##a)
 | |
| #else
 | |
| #define ulog(f, a...) do {} while (0)
 | |
| #endif
 | |
| 
 | |
| static int need_exit;
 | |
| static __u32 seq;
 | |
| 
 | |
| static int netlink_send(int s, struct cn_msg *msg)
 | |
| {
 | |
| 	struct nlmsghdr *nlh;
 | |
| 	unsigned int size;
 | |
| 	int err;
 | |
| 	char buf[128];
 | |
| 	struct cn_msg *m;
 | |
| 
 | |
| 	size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
 | |
| 
 | |
| 	nlh = (struct nlmsghdr *)buf;
 | |
| 	nlh->nlmsg_seq = seq++;
 | |
| 	nlh->nlmsg_pid = getpid();
 | |
| 	nlh->nlmsg_type = NLMSG_DONE;
 | |
| 	nlh->nlmsg_len = size;
 | |
| 	nlh->nlmsg_flags = 0;
 | |
| 
 | |
| 	m = NLMSG_DATA(nlh);
 | |
| #if 0
 | |
| 	ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
 | |
| 	       __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
 | |
| #endif
 | |
| 	memcpy(m, msg, sizeof(*m) + msg->len);
 | |
| 
 | |
| 	err = send(s, nlh, size, 0);
 | |
| 	if (err == -1)
 | |
| 		ulog("Failed to send: %s [%d].\n",
 | |
| 			strerror(errno), errno);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static void usage(void)
 | |
| {
 | |
| 	printf(
 | |
| 		"Usage: ucon [options] [output file]\n"
 | |
| 		"\n"
 | |
| 		"\t-h\tthis help screen\n"
 | |
| 		"\t-s\tsend buffers to the test module\n"
 | |
| 		"\n"
 | |
| 		"The default behavior of ucon is to subscribe to the test module\n"
 | |
| 		"and wait for state messages.  Any ones received are dumped to the\n"
 | |
| 		"specified output file (or stdout).  The test module is assumed to\n"
 | |
| 		"have an id of {%u.%u}\n"
 | |
| 		"\n"
 | |
| 		"If you get no output, then verify the cn_test module id matches\n"
 | |
| 		"the expected id above.\n"
 | |
| 		, CN_TEST_IDX, CN_TEST_VAL
 | |
| 	);
 | |
| }
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	int s;
 | |
| 	char buf[1024];
 | |
| 	int len;
 | |
| 	struct nlmsghdr *reply;
 | |
| 	struct sockaddr_nl l_local;
 | |
| 	struct cn_msg *data;
 | |
| 	FILE *out;
 | |
| 	time_t tm;
 | |
| 	struct pollfd pfd;
 | |
| 	bool send_msgs = false;
 | |
| 
 | |
| 	while ((s = getopt(argc, argv, "hs")) != -1) {
 | |
| 		switch (s) {
 | |
| 		case 's':
 | |
| 			send_msgs = true;
 | |
| 			break;
 | |
| 
 | |
| 		case 'h':
 | |
| 			usage();
 | |
| 			return 0;
 | |
| 
 | |
| 		default:
 | |
| 			/* getopt() outputs an error for us */
 | |
| 			usage();
 | |
| 			return 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (argc != optind) {
 | |
| 		out = fopen(argv[optind], "a+");
 | |
| 		if (!out) {
 | |
| 			ulog("Unable to open %s for writing: %s\n",
 | |
| 				argv[1], strerror(errno));
 | |
| 			out = stdout;
 | |
| 		}
 | |
| 	} else
 | |
| 		out = stdout;
 | |
| 
 | |
| 	memset(buf, 0, sizeof(buf));
 | |
| 
 | |
| 	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
 | |
| 	if (s == -1) {
 | |
| 		perror("socket");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	l_local.nl_family = AF_NETLINK;
 | |
| 	l_local.nl_groups = -1; /* bitmask of requested groups */
 | |
| 	l_local.nl_pid = 0;
 | |
| 
 | |
| 	ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
 | |
| 
 | |
| 	if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
 | |
| 		perror("bind");
 | |
| 		close(s);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| #if 0
 | |
| 	{
 | |
| 		int on = 0x57; /* Additional group number */
 | |
| 		setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
 | |
| 	}
 | |
| #endif
 | |
| 	if (send_msgs) {
 | |
| 		int i, j;
 | |
| 
 | |
| 		memset(buf, 0, sizeof(buf));
 | |
| 
 | |
| 		data = (struct cn_msg *)buf;
 | |
| 
 | |
| 		data->id.idx = CN_TEST_IDX;
 | |
| 		data->id.val = CN_TEST_VAL;
 | |
| 		data->seq = seq++;
 | |
| 		data->ack = 0;
 | |
| 		data->len = 0;
 | |
| 
 | |
| 		for (j=0; j<10; ++j) {
 | |
| 			for (i=0; i<1000; ++i) {
 | |
| 				len = netlink_send(s, data);
 | |
| 			}
 | |
| 
 | |
| 			ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
 | |
| 		}
 | |
| 
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	pfd.fd = s;
 | |
| 
 | |
| 	while (!need_exit) {
 | |
| 		pfd.events = POLLIN;
 | |
| 		pfd.revents = 0;
 | |
| 		switch (poll(&pfd, 1, -1)) {
 | |
| 			case 0:
 | |
| 				need_exit = 1;
 | |
| 				break;
 | |
| 			case -1:
 | |
| 				if (errno != EINTR) {
 | |
| 					need_exit = 1;
 | |
| 					break;
 | |
| 				}
 | |
| 				continue;
 | |
| 		}
 | |
| 		if (need_exit)
 | |
| 			break;
 | |
| 
 | |
| 		memset(buf, 0, sizeof(buf));
 | |
| 		len = recv(s, buf, sizeof(buf), 0);
 | |
| 		if (len == -1) {
 | |
| 			perror("recv buf");
 | |
| 			close(s);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		reply = (struct nlmsghdr *)buf;
 | |
| 
 | |
| 		switch (reply->nlmsg_type) {
 | |
| 		case NLMSG_ERROR:
 | |
| 			fprintf(out, "Error message received.\n");
 | |
| 			fflush(out);
 | |
| 			break;
 | |
| 		case NLMSG_DONE:
 | |
| 			data = (struct cn_msg *)NLMSG_DATA(reply);
 | |
| 
 | |
| 			time(&tm);
 | |
| 			fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
 | |
| 				ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
 | |
| 			fflush(out);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	close(s);
 | |
| 	return 0;
 | |
| }
 |