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