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 		free(h);
    235 		return NULL;
    236 	}
    237 	memset(&h->local, 0, sizeof(struct sockaddr_nl));
    238 	h->local.nl_family = AF_NETLINK;
    239 	h->local.nl_pid = getpid();
    240 	h->local.nl_groups = 0;
    241 	status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
    242 	if (status == -1) {
    243 		ipq_errno = IPQ_ERR_BIND;
    244 		close(h->fd);
    245 		free(h);
    246 		return NULL;
    247 	}
    248 	memset(&h->peer, 0, sizeof(struct sockaddr_nl));
    249 	h->peer.nl_family = AF_NETLINK;
    250 	h->peer.nl_pid = 0;
    251 	h->peer.nl_groups = 0;
    252 	return h;
    253 }
    254 
    255 /*
    256  * No error condition is checked here at this stage, but it may happen
    257  * if/when reliable messaging is implemented.
    258  */
    259 int ipq_destroy_handle(struct ipq_handle *h)
    260 {
    261 	if (h) {
    262 		close(h->fd);
    263 		free(h);
    264 	}
    265 	return 0;
    266 }
    267 
    268 int ipq_set_mode(const struct ipq_handle *h,
    269                  uint8_t mode, size_t range)
    270 {
    271 	struct {
    272 		struct nlmsghdr nlh;
    273 		ipq_peer_msg_t pm;
    274 	} req;
    275 
    276 	memset(&req, 0, sizeof(req));
    277 	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
    278 	req.nlh.nlmsg_flags = NLM_F_REQUEST;
    279 	req.nlh.nlmsg_type = IPQM_MODE;
    280 	req.nlh.nlmsg_pid = h->local.nl_pid;
    281 	req.pm.msg.mode.value = mode;
    282 	req.pm.msg.mode.range = range;
    283 	return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
    284 }
    285 
    286 /*
    287  * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
    288  *
    289  */
    290 ssize_t ipq_read(const struct ipq_handle *h,
    291                  unsigned char *buf, size_t len, int timeout)
    292 {
    293 	return ipq_netlink_recvfrom(h, buf, len, timeout);
    294 }
    295 
    296 int ipq_message_type(const unsigned char *buf)
    297 {
    298 	return ((struct nlmsghdr*)buf)->nlmsg_type;
    299 }
    300 
    301 int ipq_get_msgerr(const unsigned char *buf)
    302 {
    303 	struct nlmsghdr *h = (struct nlmsghdr *)buf;
    304 	struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
    305 	return -err->error;
    306 }
    307 
    308 ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
    309 {
    310 	return NLMSG_DATA((struct nlmsghdr *)(buf));
    311 }
    312 
    313 int ipq_set_verdict(const struct ipq_handle *h,
    314                     ipq_id_t id,
    315                     unsigned int verdict,
    316                     size_t data_len,
    317                     unsigned char *buf)
    318 {
    319 	unsigned char nvecs;
    320 	size_t tlen;
    321 	struct nlmsghdr nlh;
    322 	ipq_peer_msg_t pm;
    323 	struct iovec iov[3];
    324 	struct msghdr msg;
    325 
    326 	memset(&nlh, 0, sizeof(nlh));
    327 	nlh.nlmsg_flags = NLM_F_REQUEST;
    328 	nlh.nlmsg_type = IPQM_VERDICT;
    329 	nlh.nlmsg_pid = h->local.nl_pid;
    330 	memset(&pm, 0, sizeof(pm));
    331 	pm.msg.verdict.value = verdict;
    332 	pm.msg.verdict.id = id;
    333 	pm.msg.verdict.data_len = data_len;
    334 	iov[0].iov_base = &nlh;
    335 	iov[0].iov_len = sizeof(nlh);
    336 	iov[1].iov_base = &pm;
    337 	iov[1].iov_len = sizeof(pm);
    338 	tlen = sizeof(nlh) + sizeof(pm);
    339 	nvecs = 2;
    340 	if (data_len && buf) {
    341 		iov[2].iov_base = buf;
    342 		iov[2].iov_len = data_len;
    343 		tlen += data_len;
    344 		nvecs++;
    345 	}
    346 	msg.msg_name = (void *)&h->peer;
    347 	msg.msg_namelen = sizeof(h->peer);
    348 	msg.msg_iov = iov;
    349 	msg.msg_iovlen = nvecs;
    350 	msg.msg_control = NULL;
    351 	msg.msg_controllen = 0;
    352 	msg.msg_flags = 0;
    353 	nlh.nlmsg_len = tlen;
    354 	return ipq_netlink_sendmsg(h, &msg, 0);
    355 }
    356 
    357 /* Not implemented yet */
    358 int ipq_ctl(const struct ipq_handle *h, int request, ...)
    359 {
    360 	return 1;
    361 }
    362 
    363 char *ipq_errstr(void)
    364 {
    365 	return ipq_strerror(ipq_errno);
    366 }
    367 
    368 void ipq_perror(const char *s)
    369 {
    370 	if (s)
    371 		fputs(s, stderr);
    372 	else
    373 		fputs("ERROR", stderr);
    374 	if (ipq_errno)
    375 		fprintf(stderr, ": %s", ipq_errstr());
    376 	if (errno)
    377 		fprintf(stderr, ": %s", strerror(errno));
    378 	fputc('\n', stderr);
    379 }
    380