blob: dbd8e5a6b54648e19ee43a59fba854f20eb284c5 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * libipq.c
3 *
4 * IPQ userspace library.
5 *
6 * Please note that this library is still developmental, and there may
7 * be some API changes.
8 *
9 * Author: James Morris <jmorris@intercode.com.au>
10 *
11 * 07-11-2001 Modified by Fernando Anton to add support for IPv6.
12 *
13 * Copyright (c) 2000-2001 Netfilter Core Team
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 */
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/time.h>
32#include <sys/types.h>
33
34#include <libipq/libipq.h>
35#include <netinet/in.h>
36#include <linux/types.h>
37#include <linux/netfilter.h>
38
39/****************************************************************************
40 *
41 * Private interface
42 *
43 ****************************************************************************/
44
45enum {
46 IPQ_ERR_NONE = 0,
47 IPQ_ERR_IMPL,
48 IPQ_ERR_HANDLE,
49 IPQ_ERR_SOCKET,
50 IPQ_ERR_BIND,
51 IPQ_ERR_BUFFER,
52 IPQ_ERR_RECV,
53 IPQ_ERR_NLEOF,
54 IPQ_ERR_ADDRLEN,
55 IPQ_ERR_STRUNC,
56 IPQ_ERR_RTRUNC,
57 IPQ_ERR_NLRECV,
58 IPQ_ERR_SEND,
59 IPQ_ERR_SUPP,
60 IPQ_ERR_RECVBUF,
61 IPQ_ERR_TIMEOUT,
62 IPQ_ERR_PROTOCOL
63};
64#define IPQ_MAXERR IPQ_ERR_PROTOCOL
65
66struct ipq_errmap_t {
67 int errcode;
68 char *message;
69} ipq_errmap[] = {
70 { IPQ_ERR_NONE, "Unknown error" },
71 { IPQ_ERR_IMPL, "Implementation error" },
72 { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
73 { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
74 { IPQ_ERR_BIND, "Unable to bind netlink socket" },
75 { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
76 { IPQ_ERR_RECV, "Failed to receive netlink message" },
77 { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
78 { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
79 { IPQ_ERR_STRUNC, "Sent message truncated" },
80 { IPQ_ERR_RTRUNC, "Received message truncated" },
81 { IPQ_ERR_NLRECV, "Received error from netlink" },
82 { IPQ_ERR_SEND, "Failed to send netlink message" },
83 { IPQ_ERR_SUPP, "Operation not supported" },
84 { IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
85 { IPQ_ERR_TIMEOUT, "Timeout"},
86 { IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
87};
88
89static int ipq_errno = IPQ_ERR_NONE;
90
91static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
92 const void *msg, size_t len);
93
94static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
95 unsigned char *buf, size_t len,
96 int timeout);
97
98static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
99 const struct msghdr *msg,
100 unsigned int flags);
101
102static char *ipq_strerror(int errcode);
103
104static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
105 const void *msg, size_t len)
106{
107 int status = sendto(h->fd, msg, len, 0,
108 (struct sockaddr *)&h->peer, sizeof(h->peer));
109 if (status < 0)
110 ipq_errno = IPQ_ERR_SEND;
111 return status;
112}
113
114static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
115 const struct msghdr *msg,
116 unsigned int flags)
117{
118 int status = sendmsg(h->fd, msg, flags);
119 if (status < 0)
120 ipq_errno = IPQ_ERR_SEND;
121 return status;
122}
123
124static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
125 unsigned char *buf, size_t len,
126 int timeout)
127{
128 unsigned int addrlen;
129 int status;
130 struct nlmsghdr *nlh;
131
132 if (len < sizeof(struct nlmsgerr)) {
133 ipq_errno = IPQ_ERR_RECVBUF;
134 return -1;
135 }
136 addrlen = sizeof(h->peer);
137
138 if (timeout != 0) {
139 int ret;
140 struct timeval tv;
141 fd_set read_fds;
142
143 if (timeout < 0) {
144 /* non-block non-timeout */
145 tv.tv_sec = 0;
146 tv.tv_usec = 0;
147 } else {
148 tv.tv_sec = timeout / 1000000;
149 tv.tv_usec = timeout % 1000000;
150 }
151
152 FD_ZERO(&read_fds);
153 FD_SET(h->fd, &read_fds);
154 ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
155 if (ret < 0) {
156 if (errno == EINTR) {
157 return 0;
158 } else {
159 ipq_errno = IPQ_ERR_RECV;
160 return -1;
161 }
162 }
163 if (!FD_ISSET(h->fd, &read_fds)) {
164 ipq_errno = IPQ_ERR_TIMEOUT;
165 return 0;
166 }
167 }
168 status = recvfrom(h->fd, buf, len, 0,
169 (struct sockaddr *)&h->peer, &addrlen);
170 if (status < 0) {
171 ipq_errno = IPQ_ERR_RECV;
172 return status;
173 }
174 if (addrlen != sizeof(h->peer)) {
175 ipq_errno = IPQ_ERR_RECV;
176 return -1;
177 }
178 if (h->peer.nl_pid != 0) {
179 ipq_errno = IPQ_ERR_RECV;
180 return -1;
181 }
182 if (status == 0) {
183 ipq_errno = IPQ_ERR_NLEOF;
184 return -1;
185 }
186 nlh = (struct nlmsghdr *)buf;
187 if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
188 ipq_errno = IPQ_ERR_RTRUNC;
189 return -1;
190 }
191 return status;
192}
193
194static char *ipq_strerror(int errcode)
195{
196 if (errcode < 0 || errcode > IPQ_MAXERR)
197 errcode = IPQ_ERR_IMPL;
198 return ipq_errmap[errcode].message;
199}
200
201/****************************************************************************
202 *
203 * Public interface
204 *
205 ****************************************************************************/
206
207/*
208 * Create and initialise an ipq handle.
209 */
210struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
211{
212 int status;
213 struct ipq_handle *h;
214
215 h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
216 if (h == NULL) {
217 ipq_errno = IPQ_ERR_HANDLE;
218 return NULL;
219 }
220
221 memset(h, 0, sizeof(struct ipq_handle));
222
223 if (protocol == NFPROTO_IPV4)
224 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
225 else if (protocol == NFPROTO_IPV6)
226 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
227 else {
228 ipq_errno = IPQ_ERR_PROTOCOL;
229 free(h);
230 return NULL;
231 }
232
233 if (h->fd == -1) {
234 ipq_errno = IPQ_ERR_SOCKET;
235 close(h->fd);
236 free(h);
237 return NULL;
238 }
239 memset(&h->local, 0, sizeof(struct sockaddr_nl));
240 h->local.nl_family = AF_NETLINK;
241 h->local.nl_pid = getpid();
242 h->local.nl_groups = 0;
243 status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
244 if (status == -1) {
245 ipq_errno = IPQ_ERR_BIND;
246 close(h->fd);
247 free(h);
248 return NULL;
249 }
250 memset(&h->peer, 0, sizeof(struct sockaddr_nl));
251 h->peer.nl_family = AF_NETLINK;
252 h->peer.nl_pid = 0;
253 h->peer.nl_groups = 0;
254 return h;
255}
256
257/*
258 * No error condition is checked here at this stage, but it may happen
259 * if/when reliable messaging is implemented.
260 */
261int ipq_destroy_handle(struct ipq_handle *h)
262{
263 if (h) {
264 close(h->fd);
265 free(h);
266 }
267 return 0;
268}
269
270int ipq_set_mode(const struct ipq_handle *h,
271 u_int8_t mode, size_t range)
272{
273 struct {
274 struct nlmsghdr nlh;
275 ipq_peer_msg_t pm;
276 } req;
277
278 memset(&req, 0, sizeof(req));
279 req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
280 req.nlh.nlmsg_flags = NLM_F_REQUEST;
281 req.nlh.nlmsg_type = IPQM_MODE;
282 req.nlh.nlmsg_pid = h->local.nl_pid;
283 req.pm.msg.mode.value = mode;
284 req.pm.msg.mode.range = range;
285 return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
286}
287
288/*
289 * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
290 *
291 */
292ssize_t ipq_read(const struct ipq_handle *h,
293 unsigned char *buf, size_t len, int timeout)
294{
295 return ipq_netlink_recvfrom(h, buf, len, timeout);
296}
297
298int ipq_message_type(const unsigned char *buf)
299{
300 return ((struct nlmsghdr*)buf)->nlmsg_type;
301}
302
303int ipq_get_msgerr(const unsigned char *buf)
304{
305 struct nlmsghdr *h = (struct nlmsghdr *)buf;
306 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
307 return -err->error;
308}
309
310ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
311{
312 return NLMSG_DATA((struct nlmsghdr *)(buf));
313}
314
315int ipq_set_verdict(const struct ipq_handle *h,
316 ipq_id_t id,
317 unsigned int verdict,
318 size_t data_len,
319 unsigned char *buf)
320{
321 unsigned char nvecs;
322 size_t tlen;
323 struct nlmsghdr nlh;
324 ipq_peer_msg_t pm;
325 struct iovec iov[3];
326 struct msghdr msg;
327
328 memset(&nlh, 0, sizeof(nlh));
329 nlh.nlmsg_flags = NLM_F_REQUEST;
330 nlh.nlmsg_type = IPQM_VERDICT;
331 nlh.nlmsg_pid = h->local.nl_pid;
332 memset(&pm, 0, sizeof(pm));
333 pm.msg.verdict.value = verdict;
334 pm.msg.verdict.id = id;
335 pm.msg.verdict.data_len = data_len;
336 iov[0].iov_base = &nlh;
337 iov[0].iov_len = sizeof(nlh);
338 iov[1].iov_base = &pm;
339 iov[1].iov_len = sizeof(pm);
340 tlen = sizeof(nlh) + sizeof(pm);
341 nvecs = 2;
342 if (data_len && buf) {
343 iov[2].iov_base = buf;
344 iov[2].iov_len = data_len;
345 tlen += data_len;
346 nvecs++;
347 }
348 msg.msg_name = (void *)&h->peer;
349 msg.msg_namelen = sizeof(h->peer);
350 msg.msg_iov = iov;
351 msg.msg_iovlen = nvecs;
352 msg.msg_control = NULL;
353 msg.msg_controllen = 0;
354 msg.msg_flags = 0;
355 nlh.nlmsg_len = tlen;
356 return ipq_netlink_sendmsg(h, &msg, 0);
357}
358
359/* Not implemented yet */
360int ipq_ctl(const struct ipq_handle *h, int request, ...)
361{
362 return 1;
363}
364
365char *ipq_errstr(void)
366{
367 return ipq_strerror(ipq_errno);
368}
369
370void ipq_perror(const char *s)
371{
372 if (s)
373 fputs(s, stderr);
374 else
375 fputs("ERROR", stderr);
376 if (ipq_errno)
377 fprintf(stderr, ": %s", ipq_errstr());
378 if (errno)
379 fprintf(stderr, ": %s", strerror(errno));
380 fputc('\n', stderr);
381}