1/*
2 * lib/netfilter/queue.c	Netfilter Queue
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
10 */
11
12/**
13 * @ingroup nfnl
14 * @defgroup queue Queue
15 * @brief
16 * @{
17 */
18
19#include <sys/types.h>
20#include <linux/netfilter/nfnetlink_queue.h>
21
22#include <netlink-local.h>
23#include <netlink/attr.h>
24#include <netlink/netfilter/nfnl.h>
25#include <netlink/netfilter/queue.h>
26
27struct nl_sock *nfnl_queue_socket_alloc(void)
28{
29	struct nl_sock *nlsk;
30
31	nlsk = nl_socket_alloc();
32	if (nlsk)
33		nl_socket_disable_auto_ack(nlsk);
34	return nlsk;
35}
36
37static int send_queue_request(struct nl_sock *sk, struct nl_msg *msg)
38{
39	int err;
40
41	err = nl_send_auto_complete(sk, msg);
42	nlmsg_free(msg);
43	if (err < 0)
44		return err;
45
46	return wait_for_ack(sk);
47}
48
49/**
50 * @name Queue Commands
51 * @{
52 */
53
54static int build_queue_cmd_request(uint8_t family, uint16_t queuenum,
55				   uint8_t command, struct nl_msg **result)
56{
57	struct nl_msg *msg;
58	struct nfqnl_msg_config_cmd cmd;
59
60	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0,
61				   family, queuenum);
62	if (msg == NULL)
63		return -NLE_NOMEM;
64
65	cmd.pf = htons(family);
66	cmd._pad = 0;
67	cmd.command = command;
68	if (nla_put(msg, NFQA_CFG_CMD, sizeof(cmd), &cmd) < 0)
69		goto nla_put_failure;
70
71	*result = msg;
72	return 0;
73
74nla_put_failure:
75	nlmsg_free(msg);
76	return -NLE_MSGSIZE;
77}
78
79int nfnl_queue_build_pf_bind(uint8_t pf, struct nl_msg **result)
80{
81	return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_BIND, result);
82}
83
84int nfnl_queue_pf_bind(struct nl_sock *nlh, uint8_t pf)
85{
86	struct nl_msg *msg;
87	int err;
88
89	if ((err = nfnl_queue_build_pf_bind(pf, &msg)) < 0)
90		return err;
91
92	return send_queue_request(nlh, msg);
93}
94
95int nfnl_queue_build_pf_unbind(uint8_t pf, struct nl_msg **result)
96{
97	return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_UNBIND, result);
98}
99
100int nfnl_queue_pf_unbind(struct nl_sock *nlh, uint8_t pf)
101{
102	struct nl_msg *msg;
103	int err;
104
105	if ((err = nfnl_queue_build_pf_unbind(pf, &msg)) < 0)
106		return err;
107
108	return send_queue_request(nlh, msg);
109}
110
111static int nfnl_queue_build_request(const struct nfnl_queue *queue,
112				    struct nl_msg **result)
113{
114	struct nl_msg *msg;
115
116	if (!nfnl_queue_test_group(queue))
117		return -NLE_MISSING_ATTR;
118
119	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0,
120				   0, nfnl_queue_get_group(queue));
121	if (msg == NULL)
122		return -NLE_NOMEM;
123
124	if (nfnl_queue_test_maxlen(queue) &&
125	    nla_put_u32(msg, NFQA_CFG_QUEUE_MAXLEN,
126			htonl(nfnl_queue_get_maxlen(queue))) < 0)
127		goto nla_put_failure;
128
129	/* This sucks, the nfnetlink_queue interface always expects both
130	 * parameters to be present. Needs to be done properly.
131	 */
132	if (nfnl_queue_test_copy_mode(queue)) {
133		struct nfqnl_msg_config_params params;
134
135		switch (nfnl_queue_get_copy_mode(queue)) {
136		case NFNL_QUEUE_COPY_NONE:
137			params.copy_mode = NFQNL_COPY_NONE;
138			break;
139		case NFNL_QUEUE_COPY_META:
140			params.copy_mode = NFQNL_COPY_META;
141			break;
142		case NFNL_QUEUE_COPY_PACKET:
143			params.copy_mode = NFQNL_COPY_PACKET;
144			break;
145		}
146		params.copy_range = htonl(nfnl_queue_get_copy_range(queue));
147
148		if (nla_put(msg, NFQA_CFG_PARAMS, sizeof(params), &params) < 0)
149			goto nla_put_failure;
150	}
151
152	*result = msg;
153	return 0;
154
155nla_put_failure:
156	nlmsg_free(msg);
157	return -NLE_MSGSIZE;
158}
159
160int nfnl_queue_build_create_request(const struct nfnl_queue *queue,
161				    struct nl_msg **result)
162{
163	struct nfqnl_msg_config_cmd cmd;
164	int err;
165
166	if ((err = nfnl_queue_build_request(queue, result)) < 0)
167		return err;
168
169	cmd.pf = 0;
170	cmd._pad = 0;
171	cmd.command = NFQNL_CFG_CMD_BIND;
172
173	NLA_PUT(*result, NFQA_CFG_CMD, sizeof(cmd), &cmd);
174
175	return 0;
176
177nla_put_failure:
178	nlmsg_free(*result);
179	return -NLE_MSGSIZE;
180}
181
182int nfnl_queue_create(struct nl_sock *nlh, const struct nfnl_queue *queue)
183{
184	struct nl_msg *msg;
185	int err;
186
187	if ((err = nfnl_queue_build_create_request(queue, &msg)) < 0)
188		return err;
189
190	return send_queue_request(nlh, msg);
191}
192
193int nfnl_queue_build_change_request(const struct nfnl_queue *queue,
194				    struct nl_msg **result)
195{
196	return nfnl_queue_build_request(queue, result);
197}
198
199int nfnl_queue_change(struct nl_sock *nlh, const struct nfnl_queue *queue)
200{
201	struct nl_msg *msg;
202	int err;
203
204	if ((err = nfnl_queue_build_change_request(queue, &msg)) < 0)
205		return err;
206
207	return send_queue_request(nlh, msg);
208}
209
210int nfnl_queue_build_delete_request(const struct nfnl_queue *queue,
211				    struct nl_msg **result)
212{
213	if (!nfnl_queue_test_group(queue))
214		return -NLE_MISSING_ATTR;
215
216	return build_queue_cmd_request(0, nfnl_queue_get_group(queue),
217				       NFQNL_CFG_CMD_UNBIND, result);
218}
219
220int nfnl_queue_delete(struct nl_sock *nlh, const struct nfnl_queue *queue)
221{
222	struct nl_msg *msg;
223	int err;
224
225	if ((err = nfnl_queue_build_delete_request(queue, &msg)) < 0)
226		return err;
227
228	return send_queue_request(nlh, msg);
229}
230
231/** @} */
232
233static struct nl_cache_ops nfnl_queue_ops = {
234	.co_name		= "netfilter/queue",
235	.co_obj_ops		= &queue_obj_ops,
236	.co_msgtypes		= {
237		END_OF_MSGTYPES_LIST,
238	},
239};
240
241static void __init nfnl_queue_init(void)
242{
243	nl_cache_mngt_register(&nfnl_queue_ops);
244}
245
246static void __exit nfnl_queue_exit(void)
247{
248	nl_cache_mngt_unregister(&nfnl_queue_ops);
249}
250
251/** @} */
252