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