1/*
2 * libipulog.c, 1.6
3 *
4 * netfilter ULOG userspace library.
5 *
6 * (C) 2000 by Harald Welte <laforge@gnumonks.org>
7 * released under the terms of GNU GPL
8 *
9 * This library is still under development, so be aware of sudden interface
10 * changes
11 *
12 * libipulog.c,v 1.6 2001/01/30 09:28:04 laforge Exp
13 */
14
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19#include <net/if.h>
20#include <libipulog/libipulog.h>
21
22struct ipulog_handle
23{
24	int fd;
25	u_int8_t blocking;
26	struct sockaddr_nl local;
27	struct sockaddr_nl peer;
28	struct nlmsghdr* last_nlhdr;
29};
30
31/* internal */
32
33enum
34{
35	IPULOG_ERR_NONE = 0,
36	IPULOG_ERR_IMPL,
37	IPULOG_ERR_HANDLE,
38	IPULOG_ERR_SOCKET,
39	IPULOG_ERR_BIND,
40	IPULOG_ERR_RECVBUF,
41	IPULOG_ERR_RECV,
42	IPULOG_ERR_NLEOF,
43	IPULOG_ERR_TRUNC,
44	IPULOG_ERR_INVGR,
45	IPULOG_ERR_INVNL,
46};
47
48#define IPULOG_MAXERR IPULOG_ERR_INVNL
49
50static int ipulog_errno = IPULOG_ERR_NONE;
51
52struct ipulog_errmap_t
53{
54	int errcode;
55	char *message;
56} ipulog_errmap[] =
57{
58	{ IPULOG_ERR_NONE, "No error" },
59	{ IPULOG_ERR_IMPL, "Not implemented yet" },
60	{ IPULOG_ERR_HANDLE, "Unable to create netlink handle" },
61	{ IPULOG_ERR_SOCKET, "Unable to create netlink socket" },
62	{ IPULOG_ERR_BIND, "Unable to bind netlink socket" },
63	{ IPULOG_ERR_RECVBUF, "Receive buffer size invalid" },
64	{ IPULOG_ERR_RECV, "Error during netlink receive" },
65	{ IPULOG_ERR_NLEOF, "Received EOF on netlink socket" },
66	{ IPULOG_ERR_TRUNC, "Receive message truncated" },
67	{ IPULOG_ERR_INVGR, "Invalid group specified" },
68	{ IPULOG_ERR_INVNL, "Invalid netlink message" },
69};
70
71static ssize_t ipulog_netlink_recvfrom(const struct ipulog_handle *h,
72			unsigned char *buf, size_t len)
73{
74	int addrlen, status;
75	struct nlmsghdr *nlh;
76
77	if (len < sizeof(struct nlmsgerr)) {
78		ipulog_errno = IPULOG_ERR_RECVBUF;
79		return -1;
80	}
81	addrlen = sizeof(h->peer);
82	status = recvfrom(h->fd, buf, len, 0, (struct sockaddr *)&h->peer,
83			&addrlen);
84	if (status < 0)
85	{
86		ipulog_errno = IPULOG_ERR_RECV;
87		return status;
88	}
89	if (addrlen != sizeof (h->peer))
90	{
91		ipulog_errno = IPULOG_ERR_RECV;
92		return -1;
93	}
94	if (status == 0)
95	{
96		ipulog_errno = IPULOG_ERR_NLEOF;
97		return -1;
98	}
99	nlh = (struct nlmsghdr *)buf;
100	if (nlh->nlmsg_flags & MSG_TRUNC || status > len)
101	{
102		ipulog_errno = IPULOG_ERR_TRUNC;
103		return -1;
104	}
105	return status;
106}
107
108static char *ipulog_strerror(int errcode)
109{
110	if (errcode < 0 || errcode > IPULOG_MAXERR)
111		errcode = IPULOG_ERR_IMPL;
112	return ipulog_errmap[errcode].message;
113}
114
115
116/* public */
117
118/* convert a netlink group (1-32) to a group_mask suitable for create_handle */
119u_int32_t ipulog_group2gmask(u_int32_t group)
120{
121	if (group < 1 || group > 32)
122	{
123		ipulog_errno = IPULOG_ERR_INVGR;
124		return 0;
125	}
126	return (1 << (group - 1));
127}
128
129/* create a ipulog handle for the reception of packets sent to gmask */
130struct ipulog_handle *ipulog_create_handle(unsigned int gmask)
131{
132	struct ipulog_handle *h;
133	int status;
134
135	h = (struct ipulog_handle *) malloc(sizeof(struct ipulog_handle));
136	if (h == NULL)
137	{
138		ipulog_errno = IPULOG_ERR_HANDLE;
139		return NULL;
140	}
141	memset(h, 0, sizeof(struct ipulog_handle));
142	h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
143	if (h->fd == -1)
144	{
145		ipulog_errno = IPULOG_ERR_SOCKET;
146		close(h->fd);
147		free(h);
148		return NULL;
149	}
150	memset(&h->local, 0, sizeof(struct sockaddr_nl));
151	h->local.nl_family = AF_NETLINK;
152	h->local.nl_pid = getpid();
153	h->local.nl_groups = gmask;
154	status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
155	if (status == -1)
156	{
157		ipulog_errno = IPULOG_ERR_BIND;
158		close(h->fd);
159		free(h);
160		return NULL;
161	}
162	memset(&h->peer, 0, sizeof(struct sockaddr_nl));
163	h->peer.nl_family = AF_NETLINK;
164	h->peer.nl_pid = 0;
165	h->peer.nl_groups = gmask;
166
167	return h;
168}
169
170/* destroy a ipulog handle */
171void ipulog_destroy_handle(struct ipulog_handle *h)
172{
173	close(h->fd);
174	free(h);
175}
176
177
178/* do a BLOCKING read on an ipulog handle */
179ssize_t ipulog_read(struct ipulog_handle *h, unsigned char *buf,
180		    size_t len, int timeout)
181{
182	return ipulog_netlink_recvfrom(h, buf, len);
183}
184
185/* get a pointer to the actual start of the ipulog packet,
186   use this to strip netlink header */
187ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
188				     const unsigned char *buf,
189				     size_t len)
190{
191	struct nlmsghdr *nlh;
192	size_t remain_len;
193
194	/* if last header in handle not inside this buffer,
195	 * drop reference to last header */
196	if ((unsigned char *)h->last_nlhdr > (buf + len) ||
197	    (unsigned char *)h->last_nlhdr < buf) {
198		h->last_nlhdr = NULL;
199	}
200
201	if (!h->last_nlhdr) {
202		/* fist message in buffer */
203		nlh = (struct nlmsghdr *) buf;
204		if (!NLMSG_OK(nlh, len)) {
205			/* ERROR */
206			ipulog_errno = IPULOG_ERR_INVNL;
207			return NULL;
208		}
209	} else {
210		/* we are in n-th part of multilink message */
211		if (h->last_nlhdr->nlmsg_type == NLMSG_DONE) {
212			/* if last part in multilink message, return */
213			h->last_nlhdr = NULL;
214			return NULL;
215		}
216
217		/* calculate remaining lenght from lasthdr to end of buffer */
218		remain_len = (len -
219				((unsigned char *)h->last_nlhdr - buf));
220		nlh = NLMSG_NEXT(h->last_nlhdr, remain_len);
221	}
222
223	/* update last_nlhdr field */
224	if (nlh->nlmsg_flags & NLM_F_MULTI) {
225			h->last_nlhdr = nlh;
226	}
227
228	return NLMSG_DATA(nlh);
229}
230
231/* print a human readable description of the last error to stderr */
232void ipulog_perror(const char *s)
233{
234	if (s)
235		fputs(s, stderr);
236	else
237		fputs("ERROR", stderr);
238	if (ipulog_errno)
239		fprintf(stderr, ": %s", ipulog_strerror(ipulog_errno));
240	if (errno)
241		fprintf(stderr, ": %s", strerror(errno));
242	fputc('\n', stderr);
243}
244
245