summaryrefslogtreecommitdiffstats
path: root/src/callback.c
blob: ded22aa4c71b101e8c2a9bbf9986e1ecfda1781c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
 *
 * 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.
 */

#include <errno.h>
#include <libmnl/libmnl.h>

static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
{
	return MNL_CB_OK;
}

static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
{
	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);

	if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
		errno = EBADMSG; 
		return MNL_CB_ERROR;
	}
	/* Netlink subsystems returns the errno value with different signess */
	if (err->error < 0)
		errno = -err->error;
	else
		errno = err->error;

	return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
}

static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
{
	return MNL_CB_STOP;
}

static mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
	[NLMSG_NOOP]	= mnl_cb_noop,
	[NLMSG_ERROR]	= mnl_cb_error,
	[NLMSG_DONE]	= mnl_cb_stop,
	[NLMSG_OVERRUN]	= mnl_cb_noop,
};

/**
 * mnl_cb_run2 - callback runqueue for netlink messages
 * @buf: buffer that contains the netlink messages
 * @numbytes: number of bytes stored in the buffer
 * @seq: sequence number that we expect to receive
 * @portid: Netlink PortID that we expect to receive
 * @cb_data: callback handler for data messages
 * @data: pointer to data that will be passed to the data callback handler
 * @cb_ctl_array: array of custom callback handlers from control messages
 * @cb_ctl_array_len: length of the array of custom control callback handlers
 *
 * You can set the cb_ctl_array to NULL if you want to use the default control
 * callback handlers, in that case, the parameter cb_ctl_array_len is not
 * checked.
 *
 * Your callback may return three possible values:
 * 	- MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
 * 	- MNL_CB_STOP (=0): stop callback runqueue.
 *	- MNL_CB_OK (>=1): no problems has occurred.
 *
 * This function propagates the callback return value. On error, it returns
 * -1 and errno is explicitly set. If the portID is not the expected, errno
 * is set to ESRCH. If the sequence number is not the expected, errno is set
 * to EPROTO.
 */
int mnl_cb_run2(const char *buf, size_t numbytes, unsigned int seq,
		unsigned int portid, mnl_cb_t cb_data, void *data,
		mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)
{
	int ret = MNL_CB_OK, len = numbytes;
	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;

	while (mnl_nlmsg_ok(nlh, len)) {
		/* check message source */
		if (!mnl_nlmsg_portid_ok(nlh, portid)) {
			errno = ESRCH;
			return -1;
		}
		/* perform sequence tracking */
		if (!mnl_nlmsg_seq_ok(nlh, seq)) {
			errno = EPROTO;
			return -1;
		}

		/* netlink data message handling */
		if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { 
			if (cb_data){
				ret = cb_data(nlh, data);
				if (ret <= MNL_CB_STOP)
					goto out;
			}
		} else if (nlh->nlmsg_type < cb_ctl_array_len) {
			if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
				ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
				if (ret <= MNL_CB_STOP)
					goto out;
			}
		} else if (default_cb_array[nlh->nlmsg_type]) {
			ret = default_cb_array[nlh->nlmsg_type](nlh, data);
			if (ret <= MNL_CB_STOP)
				goto out;
		}
		nlh = mnl_nlmsg_next(nlh, &len);
	}
out:
	return ret;
}

/**
 * mnl_cb_run - callback runqueue for netlink messages (simplified version)
 * @buf: buffer that contains the netlink messages
 * @numbytes: number of bytes stored in the buffer
 * @seq: sequence number that we expect to receive
 * @portid: Netlink PortID that we expect to receive
 * @cb_data: callback handler for data messages
 * @data: pointer to data that will be passed to the data callback handler
 *
 * This function is like mnl_cb_run2() but it does not allow you to set
 * the control callback handlers.
 *
 * Your callback may return three possible values:
 * 	- MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
 * 	- MNL_CB_STOP (=0): stop callback runqueue.
 *	- MNL_CB_OK (>=1): no problems has occurred.
 *
 * This function propagates the callback return value.
 */
int mnl_cb_run(const char *buf, size_t numbytes, unsigned int seq,
	       unsigned int portid, mnl_cb_t cb_data, void *data)
{
	return mnl_cb_run2(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
}