Merge commit 'origin/trunk@12184' into fedora
[iptables.git] / libipq / libipq.c.netlink
1 /*
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
36 /****************************************************************************
37  *
38  * Private interface
39  *
40  ****************************************************************************/
41
42 enum {
43         IPQ_ERR_NONE = 0,
44         IPQ_ERR_IMPL,
45         IPQ_ERR_HANDLE,
46         IPQ_ERR_SOCKET,
47         IPQ_ERR_BIND,
48         IPQ_ERR_BUFFER,
49         IPQ_ERR_RECV,
50         IPQ_ERR_NLEOF,
51         IPQ_ERR_ADDRLEN,
52         IPQ_ERR_STRUNC,
53         IPQ_ERR_RTRUNC,
54         IPQ_ERR_NLRECV,
55         IPQ_ERR_SEND,
56         IPQ_ERR_SUPP,
57         IPQ_ERR_RECVBUF,
58         IPQ_ERR_TIMEOUT,
59         IPQ_ERR_PROTOCOL
60 };
61 #define IPQ_MAXERR IPQ_ERR_PROTOCOL
62
63 struct ipq_errmap_t {
64         int errcode;
65         char *message;
66 } ipq_errmap[] = {
67         { IPQ_ERR_NONE, "Unknown error" },
68         { IPQ_ERR_IMPL, "Implementation error" },
69         { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
70         { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
71         { IPQ_ERR_BIND, "Unable to bind netlink socket" },
72         { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
73         { IPQ_ERR_RECV, "Failed to receive netlink message" },
74         { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
75         { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
76         { IPQ_ERR_STRUNC, "Sent message truncated" },
77         { IPQ_ERR_RTRUNC, "Received message truncated" },
78         { IPQ_ERR_NLRECV, "Received error from netlink" },
79         { IPQ_ERR_SEND, "Failed to send netlink message" },
80         { IPQ_ERR_SUPP, "Operation not supported" },
81         { IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
82         { IPQ_ERR_TIMEOUT, "Timeout"},
83         { IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
84 };
85
86 static int ipq_errno = IPQ_ERR_NONE;
87
88 static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
89                                   const void *msg, size_t len);
90
91 static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
92                                     unsigned char *buf, size_t len,
93                                     int timeout);
94
95 static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
96                                    const struct msghdr *msg,
97                                    unsigned int flags);
98
99 static char *ipq_strerror(int errcode);
100
101 static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
102                                   const void *msg, size_t len)
103 {
104         int status = sendto(h->fd, msg, len, 0,
105                             (struct sockaddr *)&h->peer, sizeof(h->peer));
106         if (status < 0)
107                 ipq_errno = IPQ_ERR_SEND;
108         return status;
109 }
110
111 static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
112                                    const struct msghdr *msg,
113                                    unsigned int flags)
114 {
115         int status = sendmsg(h->fd, msg, flags);
116         if (status < 0)
117                 ipq_errno = IPQ_ERR_SEND;
118         return status;
119 }
120
121 static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
122                                     unsigned char *buf, size_t len,
123                                     int timeout)
124 {
125         int addrlen, status;
126         struct nlmsghdr *nlh;
127
128         if (len < sizeof(struct nlmsgerr)) {
129                 ipq_errno = IPQ_ERR_RECVBUF;
130                 return -1;
131         }
132         addrlen = sizeof(h->peer);
133
134         if (timeout != 0) {
135                 int ret;
136                 struct timeval tv;
137                 fd_set read_fds;
138                 
139                 if (timeout < 0) {
140                         /* non-block non-timeout */
141                         tv.tv_sec = 0;
142                         tv.tv_usec = 0;
143                 } else {
144                         tv.tv_sec = timeout / 1000000;
145                         tv.tv_usec = timeout % 1000000;
146                 }
147
148                 FD_ZERO(&read_fds);
149                 FD_SET(h->fd, &read_fds);
150                 ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
151                 if (ret < 0) {
152                         if (errno == EINTR) {
153                                 return 0;
154                         } else {
155                                 ipq_errno = IPQ_ERR_RECV;
156                                 return -1;
157                         }
158                 }
159                 if (!FD_ISSET(h->fd, &read_fds)) {
160                         ipq_errno = IPQ_ERR_TIMEOUT;
161                         return 0;
162                 }
163         }
164         status = recvfrom(h->fd, buf, len, 0,
165                               (struct sockaddr *)&h->peer, &addrlen);
166         if (status < 0) {
167                 ipq_errno = IPQ_ERR_RECV;
168                 return status;
169         }
170         if (addrlen != sizeof(h->peer)) {
171                 ipq_errno = IPQ_ERR_RECV;
172                 return -1;
173         }
174         if (status == 0) {
175                 ipq_errno = IPQ_ERR_NLEOF;
176                 return -1;
177         }
178         nlh = (struct nlmsghdr *)buf;
179         if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
180                 ipq_errno = IPQ_ERR_RTRUNC;
181                 return -1;
182         }
183         return status;
184 }
185
186 static char *ipq_strerror(int errcode)
187 {
188         if (errcode < 0 || errcode > IPQ_MAXERR)
189                 errcode = IPQ_ERR_IMPL;
190         return ipq_errmap[errcode].message;
191 }
192
193 /****************************************************************************
194  *
195  * Public interface
196  *
197  ****************************************************************************/
198
199 /*
200  * Create and initialise an ipq handle.
201  */
202 struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
203 {
204         int status;
205         struct ipq_handle *h;
206
207         h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
208         if (h == NULL) {
209                 ipq_errno = IPQ_ERR_HANDLE;
210                 return NULL;
211         }
212         
213         memset(h, 0, sizeof(struct ipq_handle));
214         
215         if (protocol == PF_INET)
216                 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
217         else if (protocol == PF_INET6)
218                 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
219         else {
220                 ipq_errno = IPQ_ERR_PROTOCOL;
221                 free(h);
222                 return NULL;
223         }
224         
225         if (h->fd == -1) {
226                 ipq_errno = IPQ_ERR_SOCKET;
227                 close(h->fd);
228                 free(h);
229                 return NULL;
230         }
231         memset(&h->local, 0, sizeof(struct sockaddr_nl));
232         h->local.nl_family = AF_NETLINK;
233         h->local.nl_pid = getpid();
234         h->local.nl_groups = 0;
235         status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
236         if (status == -1) {
237                 ipq_errno = IPQ_ERR_BIND;
238                 close(h->fd);
239                 free(h);
240                 return NULL;
241         }
242         memset(&h->peer, 0, sizeof(struct sockaddr_nl));
243         h->peer.nl_family = AF_NETLINK;
244         h->peer.nl_pid = 0;
245         h->peer.nl_groups = 0;
246         return h;
247 }
248
249 /*
250  * No error condition is checked here at this stage, but it may happen
251  * if/when reliable messaging is implemented.
252  */
253 int ipq_destroy_handle(struct ipq_handle *h)
254 {
255         if (h) {
256                 close(h->fd);
257                 free(h);
258         }
259         return 0;
260 }
261
262 int ipq_set_mode(const struct ipq_handle *h,
263                  u_int8_t mode, size_t range)
264 {
265         struct {
266                 struct nlmsghdr nlh;
267                 ipq_peer_msg_t pm;
268         } req;
269
270         memset(&req, 0, sizeof(req));
271         req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
272         req.nlh.nlmsg_flags = NLM_F_REQUEST;
273         req.nlh.nlmsg_type = IPQM_MODE;
274         req.nlh.nlmsg_pid = h->local.nl_pid;
275         req.pm.msg.mode.value = mode;
276         req.pm.msg.mode.range = range;
277         return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
278 }
279
280 /*
281  * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
282  *
283  */
284 ssize_t ipq_read(const struct ipq_handle *h,
285                  unsigned char *buf, size_t len, int timeout)
286 {
287         return ipq_netlink_recvfrom(h, buf, len, timeout);
288 }
289
290 int ipq_message_type(const unsigned char *buf)
291 {
292         return ((struct nlmsghdr*)buf)->nlmsg_type;
293 }
294
295 int ipq_get_msgerr(const unsigned char *buf)
296 {
297         struct nlmsghdr *h = (struct nlmsghdr *)buf;
298         struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
299         return -err->error;
300 }
301
302 ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
303 {
304         return NLMSG_DATA((struct nlmsghdr *)(buf));
305 }
306
307 int ipq_set_verdict(const struct ipq_handle *h,
308                     ipq_id_t id,
309                     unsigned int verdict,
310                     size_t data_len,
311                     unsigned char *buf)
312 {
313         unsigned char nvecs;
314         size_t tlen;
315         struct nlmsghdr nlh;
316         ipq_peer_msg_t pm;
317         struct iovec iov[3];
318         struct msghdr msg;
319
320         memset(&nlh, 0, sizeof(nlh));
321         nlh.nlmsg_flags = NLM_F_REQUEST;
322         nlh.nlmsg_type = IPQM_VERDICT;
323         nlh.nlmsg_pid = h->local.nl_pid;
324         memset(&pm, 0, sizeof(pm));
325         pm.msg.verdict.value = verdict;
326         pm.msg.verdict.id = id;
327         pm.msg.verdict.data_len = data_len;
328         iov[0].iov_base = &nlh;
329         iov[0].iov_len = sizeof(nlh);
330         iov[1].iov_base = &pm;
331         iov[1].iov_len = sizeof(pm);
332         tlen = sizeof(nlh) + sizeof(pm);
333         nvecs = 2;
334         if (data_len && buf) {
335                 iov[2].iov_base = buf;
336                 iov[2].iov_len = data_len;
337                 tlen += data_len;
338                 nvecs++;
339         }
340         msg.msg_name = (void *)&h->peer;
341         msg.msg_namelen = sizeof(h->peer);
342         msg.msg_iov = iov;
343         msg.msg_iovlen = nvecs;
344         msg.msg_control = NULL;
345         msg.msg_controllen = 0;
346         msg.msg_flags = 0;
347         nlh.nlmsg_len = tlen;
348         return ipq_netlink_sendmsg(h, &msg, 0);
349 }
350
351 /* Not implemented yet */
352 int ipq_ctl(const struct ipq_handle *h, int request, ...)
353 {
354         return 1;
355 }
356
357 char *ipq_errstr(void)
358 {
359         return ipq_strerror(ipq_errno);
360 }
361
362 void ipq_perror(const char *s)
363 {
364         if (s)
365                 fputs(s, stderr);
366         else
367                 fputs("ERROR", stderr);
368         if (ipq_errno)
369                 fprintf(stderr, ": %s", ipq_errstr());
370         if (errno)
371                 fprintf(stderr, ": %s", strerror(errno));
372         fputc('\n', stderr);
373 }