Home | History | Annotate | Download | only in libipq
      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 (at) 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 	unsigned int addrlen;
    126 	int status;
    127 	struct nlmsghdr *nlh;
    128 
    129 	if (len < sizeof(struct nlmsgerr)) {
    130 		ipq_errno = IPQ_ERR_RECVBUF;
    131 		return -1;
    132 	}
    133 	addrlen = sizeof(h->peer);
    134 
    135 	if (timeout != 0) {
    136 		int ret;
    137 		struct timeval tv;
    138 		fd_set read_fds;
    139 
    140 		if (timeout < 0) {
    141 			/* non-block non-timeout */
    142 			tv.tv_sec = 0;
    143 			tv.tv_usec = 0;
    144 		} else {
    145 			tv.tv_sec = timeout / 1000000;
    146 			tv.tv_usec = timeout % 1000000;
    147 		}
    148 
    149 		FD_ZERO(&read_fds);
    150 		FD_SET(h->fd, &read_fds);
    151 		ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
    152 		if (ret < 0) {
    153 			if (errno == EINTR) {
    154 				return 0;
    155 			} else {
    156 				ipq_errno = IPQ_ERR_RECV;
    157 				return -1;
    158 			}
    159 		}
    160 		if (!FD_ISSET(h->fd, &read_fds)) {
    161 			ipq_errno = IPQ_ERR_TIMEOUT;
    162 			return 0;
    163 		}
    164 	}
    165 	status = recvfrom(h->fd, buf, len, 0,
    166 	                      (struct sockaddr *)&h->peer, &addrlen);
    167 	if (status < 0) {
    168 		ipq_errno = IPQ_ERR_RECV;
    169 		return status;
    170 	}
    171 	if (addrlen != sizeof(h->peer)) {
    172 		ipq_errno = IPQ_ERR_RECV;
    173 		return -1;
    174 	}
    175 	if (h->peer.nl_pid != 0) {
    176 		ipq_errno = IPQ_ERR_RECV;
    177 		return -1;
    178 	}
    179 	if (status == 0) {
    180 		ipq_errno = IPQ_ERR_NLEOF;
    181 		return -1;
    182 	}
    183 	nlh = (struct nlmsghdr *)buf;
    184 	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
    185 		ipq_errno = IPQ_ERR_RTRUNC;
    186 		return -1;
    187 	}
    188 	return status;
    189 }
    190 
    191 static char *ipq_strerror(int errcode)
    192 {
    193 	if (errcode < 0 || errcode > IPQ_MAXERR)
    194 		errcode = IPQ_ERR_IMPL;
    195 	return ipq_errmap[errcode].message;
    196 }
    197 
    198 /****************************************************************************
    199  *
    200  * Public interface
    201  *
    202  ****************************************************************************/
    203 
    204 /*
    205  * Create and initialise an ipq handle.
    206  */
    207 struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
    208 {
    209 	int status;
    210 	struct ipq_handle *h;
    211 
    212 	h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
    213 	if (h == NULL) {
    214 		ipq_errno = IPQ_ERR_HANDLE;
    215 		return NULL;
    216 	}
    217 
    218 	memset(h, 0, sizeof(struct ipq_handle));
    219 
    220         if (protocol == PF_INET)
    221                 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
    222         else if (protocol == PF_INET6)
    223                 h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
    224         else {
    225 		ipq_errno = IPQ_ERR_PROTOCOL;
    226 		free(h);
    227 		return NULL;
    228         }
    229 
    230 	if (h->fd == -1) {
    231 		ipq_errno = IPQ_ERR_SOCKET;
    232 		close(h->fd);
    233 		free(h);
    234 		return NULL;
    235 	}
    236 	memset(&h->local, 0, sizeof(struct sockaddr_nl));
    237 	h->local.nl_family = AF_NETLINK;
    238 	h->local.nl_pid = getpid();
    239 	h->local.nl_groups = 0;
    240 	status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
    241 	if (status == -1) {
    242 		ipq_errno = IPQ_ERR_BIND;
    243 		close(h->fd);
    244 		free(h);
    245 		return NULL;
    246 	}
    247 	memset(&h->peer, 0, sizeof(struct sockaddr_nl));
    248 	h->peer.nl_family = AF_NETLINK;
    249 	h->peer.nl_pid = 0;
    250 	h->peer.nl_groups = 0;
    251 	return h;
    252 }
    253 
    254 /*
    255  * No error condition is checked here at this stage, but it may happen
    256  * if/when reliable messaging is implemented.
    257  */
    258 int ipq_destroy_handle(struct ipq_handle *h)
    259 {
    260 	if (h) {
    261 		close(h->fd);
    262 		free(h);
    263 	}
    264 	return 0;
    265 }
    266 
    267 int ipq_set_mode(const struct ipq_handle *h,
    268                  u_int8_t mode, size_t range)
    269 {
    270 	struct {
    271 		struct nlmsghdr nlh;
    272 		ipq_peer_msg_t pm;
    273 	} req;
    274 
    275 	memset(&req, 0, sizeof(req));
    276 	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
    277 	req.nlh.nlmsg_flags = NLM_F_REQUEST;
    278 	req.nlh.nlmsg_type = IPQM_MODE;
    279 	req.nlh.nlmsg_pid = h->local.nl_pid;
    280 	req.pm.msg.mode.value = mode;
    281 	req.pm.msg.mode.range = range;
    282 	return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
    283 }
    284 
    285 /*
    286  * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
    287  *
    288  */
    289 ssize_t ipq_read(const struct ipq_handle *h,
    290                  unsigned char *buf, size_t len, int timeout)
    291 {
    292 	return ipq_netlink_recvfrom(h, buf, len, timeout);
    293 }
    294 
    295 int ipq_message_type(const unsigned char *buf)
    296 {
    297 	return ((struct nlmsghdr*)buf)->nlmsg_type;
    298 }
    299 
    300 int ipq_get_msgerr(const unsigned char *buf)
    301 {
    302 	struct nlmsghdr *h = (struct nlmsghdr *)buf;
    303 	struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
    304 	return -err->error;
    305 }
    306 
    307 ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
    308 {
    309 	return NLMSG_DATA((struct nlmsghdr *)(buf));
    310 }
    311 
    312 int ipq_set_verdict(const struct ipq_handle *h,
    313                     ipq_id_t id,
    314                     unsigned int verdict,
    315                     size_t data_len,
    316                     unsigned char *buf)
    317 {
    318 	unsigned char nvecs;
    319 	size_t tlen;
    320 	struct nlmsghdr nlh;
    321 	ipq_peer_msg_t pm;
    322 	struct iovec iov[3];
    323 	struct msghdr msg;
    324 
    325 	memset(&nlh, 0, sizeof(nlh));
    326 	nlh.nlmsg_flags = NLM_F_REQUEST;
    327 	nlh.nlmsg_type = IPQM_VERDICT;
    328 	nlh.nlmsg_pid = h->local.nl_pid;
    329 	memset(&pm, 0, sizeof(pm));
    330 	pm.msg.verdict.value = verdict;
    331 	pm.msg.verdict.id = id;
    332 	pm.msg.verdict.data_len = data_len;
    333 	iov[0].iov_base = &nlh;
    334 	iov[0].iov_len = sizeof(nlh);
    335 	iov[1].iov_base = &pm;
    336 	iov[1].iov_len = sizeof(pm);
    337 	tlen = sizeof(nlh) + sizeof(pm);
    338 	nvecs = 2;
    339 	if (data_len && buf) {
    340 		iov[2].iov_base = buf;
    341 		iov[2].iov_len = data_len;
    342 		tlen += data_len;
    343 		nvecs++;
    344 	}
    345 	msg.msg_name = (void *)&h->peer;
    346 	msg.msg_namelen = sizeof(h->peer);
    347 	msg.msg_iov = iov;
    348 	msg.msg_iovlen = nvecs;
    349 	msg.msg_control = NULL;
    350 	msg.msg_controllen = 0;
    351 	msg.msg_flags = 0;
    352 	nlh.nlmsg_len = tlen;
    353 	return ipq_netlink_sendmsg(h, &msg, 0);
    354 }
    355 
    356 /* Not implemented yet */
    357 int ipq_ctl(const struct ipq_handle *h, int request, ...)
    358 {
    359 	return 1;
    360 }
    361 
    362 char *ipq_errstr(void)
    363 {
    364 	return ipq_strerror(ipq_errno);
    365 }
    366 
    367 void ipq_perror(const char *s)
    368 {
    369 	if (s)
    370 		fputs(s, stderr);
    371 	else
    372 		fputs("ERROR", stderr);
    373 	if (ipq_errno)
    374 		fprintf(stderr, ": %s", ipq_errstr());
    375 	if (errno)
    376 		fprintf(stderr, ": %s", strerror(errno));
    377 	fputc('\n', stderr);
    378 }
    379