summaryrefslogtreecommitdiffstats
path: root/src/callback.c
blob: f023401ece97f977f05e8ba7bb0de0645f0a9d66 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
 * (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 Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 */

#include <errno.h>
#include <libmnl/libmnl.h>
#include "internal.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 const 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,
};

static inline int
__mnl_cb_run(const void *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;
	const struct nlmsghdr *nlh = 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;
		}

		/* dump was interrupted */
		if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
			errno = EINTR;
			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;
}

/**
 * \defgroup callback Callback helpers
 * @{
 */

/**
 * mnl_cb_run2 - callback runqueue for netlink messages
 * \param buf buffer that contains the netlink messages
 * \param numbytes number of bytes stored in the buffer
 * \param seq sequence number that we expect to receive
 * \param portid Netlink PortID that we expect to receive
 * \param cb_data callback handler for data messages
 * \param data pointer to data that will be passed to the data callback handler
 * \param cb_ctl_array array of custom callback handlers from control messages
 * \param cb_ctl_array_len array length 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 problem 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. If the dump was interrupted, errno is set to EINTR and you should
 * request a new fresh dump again.
 */
int
mnl_cb_run2(const void *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)
{
	return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
			    cb_ctl_array, cb_ctl_array_len);
}
EXPORT_SYMBOL(mnl_cb_run2);

/**
 * mnl_cb_run - callback runqueue for netlink messages (simplified version)
 * \param buf buffer that contains the netlink messages
 * \param numbytes number of bytes stored in the buffer
 * \param seq sequence number that we expect to receive
 * \param portid Netlink PortID that we expect to receive
 * \param cb_data callback handler for data messages
 * \param 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 void *buf, size_t numbytes, unsigned int seq,
	   unsigned int portid, mnl_cb_t cb_data, void *data)
{
	return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
}
EXPORT_SYMBOL(mnl_cb_run);

/**
 * @}
 */