1230832Sgnn/* 2230832Sgnn * Copyright (c) 2011 Jakub Zawadzki 3230832Sgnn * All rights reserved. 4230832Sgnn * 5230832Sgnn * Redistribution and use in source and binary forms, with or without 6230832Sgnn * modification, are permitted provided that the following conditions 7230832Sgnn * are met: 8230832Sgnn * 9230832Sgnn * 1. Redistributions of source code must retain the above copyright 10230832Sgnn * notice, this list of conditions and the following disclaimer. 11230832Sgnn * 2. Redistributions in binary form must reproduce the above copyright 12230832Sgnn * notice, this list of conditions and the following disclaimer in the 13230832Sgnn * documentation and/or other materials provided with the distribution. 14230832Sgnn * 3. The name of the author may not be used to endorse or promote 15230832Sgnn * products derived from this software without specific prior written 16230832Sgnn * permission. 17230832Sgnn * 18230832Sgnn * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19230832Sgnn * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20230832Sgnn * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21230832Sgnn * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22230832Sgnn * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23230832Sgnn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24230832Sgnn * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25230832Sgnn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26230832Sgnn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27230832Sgnn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28230832Sgnn * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29230832Sgnn */ 30230832Sgnn 31230832Sgnn#ifdef HAVE_CONFIG_H 32230832Sgnn#include "config.h" 33230832Sgnn#endif 34230832Sgnn 35230832Sgnn#include "pcap-int.h" 36230832Sgnn 37230832Sgnn#ifdef NEED_STRERROR_H 38230832Sgnn#include "strerror.h" 39230832Sgnn#endif 40230832Sgnn 41230832Sgnn#include <errno.h> 42230832Sgnn#include <stdlib.h> 43230832Sgnn#include <unistd.h> 44230832Sgnn#include <string.h> 45230832Sgnn#include <sys/socket.h> 46230832Sgnn#include <arpa/inet.h> 47230832Sgnn 48230832Sgnn#include <time.h> 49230832Sgnn#include <sys/time.h> 50230832Sgnn#include <netinet/in.h> 51230832Sgnn#include <linux/types.h> 52230832Sgnn 53230832Sgnn#include <linux/netlink.h> 54251129Sdelphij#include <linux/netfilter.h> 55230832Sgnn#include <linux/netfilter/nfnetlink.h> 56230832Sgnn#include <linux/netfilter/nfnetlink_log.h> 57251129Sdelphij#include <linux/netfilter/nfnetlink_queue.h> 58230832Sgnn 59251129Sdelphij/* NOTE: if your program drops privilages after pcap_activate() it WON'T work with nfqueue. 60251129Sdelphij * It took me quite some time to debug ;/ 61251129Sdelphij * 62251129Sdelphij * Sending any data to nfnetlink socket requires CAP_NET_ADMIN privilages, 63251129Sdelphij * and in nfqueue we need to send verdict reply after recving packet. 64251129Sdelphij * 65251129Sdelphij * In tcpdump you can disable dropping privilages with -Z root 66251129Sdelphij */ 67251129Sdelphij 68230832Sgnn#include "pcap-netfilter-linux.h" 69230832Sgnn 70230832Sgnn#define HDR_LENGTH (NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct nfgenmsg)))) 71230832Sgnn 72230832Sgnn#define NFLOG_IFACE "nflog" 73251129Sdelphij#define NFQUEUE_IFACE "nfqueue" 74230832Sgnn 75251129Sdelphijtypedef enum { OTHER = -1, NFLOG, NFQUEUE } nftype_t; 76251129Sdelphij 77251129Sdelphijstatic int nfqueue_send_verdict(const pcap_t *handle, u_int16_t group_id, u_int32_t id, u_int32_t verdict); 78251129Sdelphij 79230832Sgnnstatic int 80251129Sdelphijnetfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) 81230832Sgnn{ 82230832Sgnn const unsigned char *buf; 83230832Sgnn int count = 0; 84230832Sgnn int len; 85230832Sgnn 86230832Sgnn /* ignore interrupt system call error */ 87230832Sgnn do { 88230832Sgnn len = recv(handle->fd, handle->buffer, handle->bufsize, 0); 89230832Sgnn if (handle->break_loop) { 90230832Sgnn handle->break_loop = 0; 91230832Sgnn return -2; 92230832Sgnn } 93230832Sgnn } while ((len == -1) && (errno == EINTR)); 94230832Sgnn 95230832Sgnn if (len < 0) { 96230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", errno, pcap_strerror(errno)); 97230832Sgnn return -1; 98230832Sgnn } 99230832Sgnn 100230832Sgnn buf = handle->buffer; 101230832Sgnn while (len >= NLMSG_SPACE(0)) { 102230832Sgnn const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf; 103230832Sgnn u_int32_t msg_len; 104251129Sdelphij nftype_t type = OTHER; 105230832Sgnn 106230832Sgnn if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) { 107230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len); 108230832Sgnn return -1; 109230832Sgnn } 110230832Sgnn 111230832Sgnn if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && 112230832Sgnn NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) 113251129Sdelphij type = NFLOG; 114251129Sdelphij 115251129Sdelphij if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && 116251129Sdelphij NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET) 117251129Sdelphij type = NFQUEUE; 118251129Sdelphij 119251129Sdelphij if (type != OTHER) { 120230832Sgnn const unsigned char *payload = NULL; 121230832Sgnn struct pcap_pkthdr pkth; 122230832Sgnn 123251129Sdelphij const struct nfgenmsg *nfg; 124251129Sdelphij int id = 0; 125251129Sdelphij 126230832Sgnn if (handle->linktype != DLT_NFLOG) { 127230832Sgnn const struct nfattr *payload_attr = NULL; 128230832Sgnn 129230832Sgnn if (nlh->nlmsg_len < HDR_LENGTH) { 130230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); 131230832Sgnn return -1; 132230832Sgnn } 133230832Sgnn 134251129Sdelphij nfg = NLMSG_DATA(nlh); 135230832Sgnn if (nlh->nlmsg_len > HDR_LENGTH) { 136251129Sdelphij struct nfattr *attr = NFM_NFA(nfg); 137230832Sgnn int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); 138230832Sgnn 139230832Sgnn while (NFA_OK(attr, attr_len)) { 140251129Sdelphij if (type == NFQUEUE) { 141251129Sdelphij switch (NFA_TYPE(attr)) { 142251129Sdelphij case NFQA_PACKET_HDR: 143251129Sdelphij { 144251129Sdelphij const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr); 145251129Sdelphij 146251129Sdelphij id = ntohl(pkt_hdr->packet_id); 147251129Sdelphij break; 148251129Sdelphij } 149251129Sdelphij case NFQA_PAYLOAD: 150251129Sdelphij payload_attr = attr; 151251129Sdelphij break; 152251129Sdelphij } 153251129Sdelphij 154251129Sdelphij } else if (type == NFLOG) { 155251129Sdelphij switch (NFA_TYPE(attr)) { 156251129Sdelphij case NFULA_PAYLOAD: 157251129Sdelphij payload_attr = attr; 158251129Sdelphij break; 159251129Sdelphij } 160230832Sgnn } 161230832Sgnn attr = NFA_NEXT(attr, attr_len); 162230832Sgnn } 163230832Sgnn } 164230832Sgnn 165230832Sgnn if (payload_attr) { 166230832Sgnn payload = NFA_DATA(payload_attr); 167230832Sgnn pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); 168230832Sgnn } 169230832Sgnn 170230832Sgnn } else { 171230832Sgnn payload = NLMSG_DATA(nlh); 172230832Sgnn pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); 173230832Sgnn } 174230832Sgnn 175230832Sgnn if (payload) { 176230832Sgnn /* pkth.caplen = min (payload_len, handle->snapshot); */ 177230832Sgnn 178230832Sgnn gettimeofday(&pkth.ts, NULL); 179230832Sgnn if (handle->fcode.bf_insns == NULL || 180230832Sgnn bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) 181230832Sgnn { 182230832Sgnn handle->md.packets_read++; 183230832Sgnn callback(user, &pkth, payload); 184230832Sgnn count++; 185230832Sgnn } 186230832Sgnn } 187251129Sdelphij 188251129Sdelphij if (type == NFQUEUE) { 189251129Sdelphij /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */ 190251129Sdelphij nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT); 191251129Sdelphij } 192230832Sgnn } 193230832Sgnn 194230832Sgnn msg_len = NLMSG_ALIGN(nlh->nlmsg_len); 195230832Sgnn if (msg_len > len) 196230832Sgnn msg_len = len; 197230832Sgnn 198230832Sgnn len -= msg_len; 199230832Sgnn buf += msg_len; 200230832Sgnn } 201230832Sgnn return count; 202230832Sgnn} 203230832Sgnn 204230832Sgnnstatic int 205230832Sgnnnetfilter_set_datalink(pcap_t *handle, int dlt) 206230832Sgnn{ 207230832Sgnn handle->linktype = dlt; 208230832Sgnn return 0; 209230832Sgnn} 210230832Sgnn 211230832Sgnnstatic int 212230832Sgnnnetfilter_stats_linux(pcap_t *handle, struct pcap_stat *stats) 213230832Sgnn{ 214230832Sgnn stats->ps_recv = handle->md.packets_read; 215230832Sgnn stats->ps_drop = 0; 216230832Sgnn stats->ps_ifdrop = 0; 217230832Sgnn return 0; 218230832Sgnn} 219230832Sgnn 220230832Sgnnstatic int 221230832Sgnnnetfilter_inject_linux(pcap_t *handle, const void *buf, size_t size) 222230832Sgnn{ 223230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on netfilter devices"); 224230832Sgnn return (-1); 225230832Sgnn} 226230832Sgnn 227230832Sgnnstruct my_nfattr { 228230832Sgnn u_int16_t nfa_len; 229230832Sgnn u_int16_t nfa_type; 230230832Sgnn void *data; 231230832Sgnn}; 232230832Sgnn 233230832Sgnnstatic int 234251129Sdelphijnetfilter_send_config_msg(const pcap_t *handle, u_int16_t msg_type, int ack, u_int8_t family, u_int16_t res_id, const struct my_nfattr *mynfa) 235230832Sgnn{ 236230832Sgnn char buf[1024] __attribute__ ((aligned)); 237230832Sgnn 238230832Sgnn struct nlmsghdr *nlh = (struct nlmsghdr *) buf; 239230832Sgnn struct nfgenmsg *nfg = (struct nfgenmsg *) (buf + sizeof(struct nlmsghdr)); 240230832Sgnn 241230832Sgnn struct sockaddr_nl snl; 242230832Sgnn static unsigned int seq_id; 243230832Sgnn 244230832Sgnn if (!seq_id) 245230832Sgnn seq_id = time(NULL); 246230832Sgnn ++seq_id; 247230832Sgnn 248230832Sgnn nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); 249251129Sdelphij nlh->nlmsg_type = msg_type; 250251129Sdelphij nlh->nlmsg_flags = NLM_F_REQUEST | (ack ? NLM_F_ACK : 0); 251230832Sgnn nlh->nlmsg_pid = 0; /* to kernel */ 252230832Sgnn nlh->nlmsg_seq = seq_id; 253230832Sgnn 254230832Sgnn nfg->nfgen_family = family; 255230832Sgnn nfg->version = NFNETLINK_V0; 256230832Sgnn nfg->res_id = htons(res_id); 257230832Sgnn 258230832Sgnn if (mynfa) { 259230832Sgnn struct nfattr *nfa = (struct nfattr *) (buf + NLMSG_ALIGN(nlh->nlmsg_len)); 260230832Sgnn 261230832Sgnn nfa->nfa_type = mynfa->nfa_type; 262230832Sgnn nfa->nfa_len = NFA_LENGTH(mynfa->nfa_len); 263230832Sgnn memcpy(NFA_DATA(nfa), mynfa->data, mynfa->nfa_len); 264230832Sgnn nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NFA_ALIGN(nfa->nfa_len); 265230832Sgnn } 266230832Sgnn 267230832Sgnn memset(&snl, 0, sizeof(snl)); 268230832Sgnn snl.nl_family = AF_NETLINK; 269230832Sgnn 270230832Sgnn if (sendto(handle->fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *) &snl, sizeof(snl)) == -1) 271230832Sgnn return -1; 272230832Sgnn 273251129Sdelphij if (!ack) 274251129Sdelphij return 0; 275251129Sdelphij 276230832Sgnn /* waiting for reply loop */ 277230832Sgnn do { 278230832Sgnn socklen_t addrlen = sizeof(snl); 279230832Sgnn int len; 280230832Sgnn 281230832Sgnn /* ignore interrupt system call error */ 282230832Sgnn do { 283230832Sgnn len = recvfrom(handle->fd, buf, sizeof(buf), 0, (struct sockaddr *) &snl, &addrlen); 284230832Sgnn } while ((len == -1) && (errno == EINTR)); 285230832Sgnn 286230832Sgnn if (len <= 0) 287230832Sgnn return len; 288230832Sgnn 289230832Sgnn if (addrlen != sizeof(snl) || snl.nl_family != AF_NETLINK) { 290230832Sgnn errno = EINVAL; 291230832Sgnn return -1; 292230832Sgnn } 293230832Sgnn 294230832Sgnn nlh = (struct nlmsghdr *) buf; 295230832Sgnn if (snl.nl_pid != 0 || seq_id != nlh->nlmsg_seq) /* if not from kernel or wrong sequence skip */ 296230832Sgnn continue; 297230832Sgnn 298230832Sgnn while (len >= NLMSG_SPACE(0) && NLMSG_OK(nlh, len)) { 299230832Sgnn if (nlh->nlmsg_type == NLMSG_ERROR || (nlh->nlmsg_type == NLMSG_DONE && nlh->nlmsg_flags & NLM_F_MULTI)) { 300230832Sgnn if (nlh->nlmsg_len < NLMSG_ALIGN(sizeof(struct nlmsgerr))) { 301230832Sgnn errno = EBADMSG; 302230832Sgnn return -1; 303230832Sgnn } 304230832Sgnn errno = -(*((int *)NLMSG_DATA(nlh))); 305230832Sgnn return (errno == 0) ? 0 : -1; 306230832Sgnn } 307230832Sgnn nlh = NLMSG_NEXT(nlh, len); 308230832Sgnn } 309230832Sgnn } while (1); 310230832Sgnn 311230832Sgnn return -1; /* never here */ 312230832Sgnn} 313230832Sgnn 314230832Sgnnstatic int 315251129Sdelphijnflog_send_config_msg(const pcap_t *handle, u_int8_t family, u_int16_t group_id, const struct my_nfattr *mynfa) 316251129Sdelphij{ 317251129Sdelphij return netfilter_send_config_msg(handle, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, 1, family, group_id, mynfa); 318251129Sdelphij} 319251129Sdelphij 320251129Sdelphijstatic int 321230832Sgnnnflog_send_config_cmd(const pcap_t *handle, u_int16_t group_id, u_int8_t cmd, u_int8_t family) 322230832Sgnn{ 323230832Sgnn struct nfulnl_msg_config_cmd msg; 324230832Sgnn struct my_nfattr nfa; 325230832Sgnn 326230832Sgnn msg.command = cmd; 327230832Sgnn 328230832Sgnn nfa.data = &msg; 329230832Sgnn nfa.nfa_type = NFULA_CFG_CMD; 330230832Sgnn nfa.nfa_len = sizeof(msg); 331230832Sgnn 332230832Sgnn return nflog_send_config_msg(handle, family, group_id, &nfa); 333230832Sgnn} 334230832Sgnn 335230832Sgnnstatic int 336230832Sgnnnflog_send_config_mode(const pcap_t *handle, u_int16_t group_id, u_int8_t copy_mode, u_int32_t copy_range) 337230832Sgnn{ 338230832Sgnn struct nfulnl_msg_config_mode msg; 339230832Sgnn struct my_nfattr nfa; 340230832Sgnn 341230832Sgnn msg.copy_range = htonl(copy_range); 342230832Sgnn msg.copy_mode = copy_mode; 343230832Sgnn 344230832Sgnn nfa.data = &msg; 345230832Sgnn nfa.nfa_type = NFULA_CFG_MODE; 346230832Sgnn nfa.nfa_len = sizeof(msg); 347230832Sgnn 348230832Sgnn return nflog_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 349230832Sgnn} 350230832Sgnn 351230832Sgnnstatic int 352251129Sdelphijnfqueue_send_verdict(const pcap_t *handle, u_int16_t group_id, u_int32_t id, u_int32_t verdict) 353230832Sgnn{ 354251129Sdelphij struct nfqnl_msg_verdict_hdr msg; 355251129Sdelphij struct my_nfattr nfa; 356251129Sdelphij 357251129Sdelphij msg.id = htonl(id); 358251129Sdelphij msg.verdict = htonl(verdict); 359251129Sdelphij 360251129Sdelphij nfa.data = &msg; 361251129Sdelphij nfa.nfa_type = NFQA_VERDICT_HDR; 362251129Sdelphij nfa.nfa_len = sizeof(msg); 363251129Sdelphij 364251129Sdelphij return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT, 0, AF_UNSPEC, group_id, &nfa); 365251129Sdelphij} 366251129Sdelphij 367251129Sdelphijstatic int 368251129Sdelphijnfqueue_send_config_msg(const pcap_t *handle, u_int8_t family, u_int16_t group_id, const struct my_nfattr *mynfa) 369251129Sdelphij{ 370251129Sdelphij return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG, 1, family, group_id, mynfa); 371251129Sdelphij} 372251129Sdelphij 373251129Sdelphijstatic int 374251129Sdelphijnfqueue_send_config_cmd(const pcap_t *handle, u_int16_t group_id, u_int8_t cmd, u_int16_t pf) 375251129Sdelphij{ 376251129Sdelphij struct nfqnl_msg_config_cmd msg; 377251129Sdelphij struct my_nfattr nfa; 378251129Sdelphij 379251129Sdelphij msg.command = cmd; 380251129Sdelphij msg.pf = htons(pf); 381251129Sdelphij 382251129Sdelphij nfa.data = &msg; 383251129Sdelphij nfa.nfa_type = NFQA_CFG_CMD; 384251129Sdelphij nfa.nfa_len = sizeof(msg); 385251129Sdelphij 386251129Sdelphij return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 387251129Sdelphij} 388251129Sdelphij 389251129Sdelphijstatic int 390251129Sdelphijnfqueue_send_config_mode(const pcap_t *handle, u_int16_t group_id, u_int8_t copy_mode, u_int32_t copy_range) 391251129Sdelphij{ 392251129Sdelphij struct nfqnl_msg_config_params msg; 393251129Sdelphij struct my_nfattr nfa; 394251129Sdelphij 395251129Sdelphij msg.copy_range = htonl(copy_range); 396251129Sdelphij msg.copy_mode = copy_mode; 397251129Sdelphij 398251129Sdelphij nfa.data = &msg; 399251129Sdelphij nfa.nfa_type = NFQA_CFG_PARAMS; 400251129Sdelphij nfa.nfa_len = sizeof(msg); 401251129Sdelphij 402251129Sdelphij return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 403251129Sdelphij} 404251129Sdelphij 405251129Sdelphijstatic int 406251129Sdelphijnetfilter_activate(pcap_t* handle) 407251129Sdelphij{ 408230832Sgnn const char *dev = handle->opt.source; 409230832Sgnn unsigned short groups[32]; 410230832Sgnn int group_count = 0; 411251129Sdelphij nftype_t type = OTHER; 412230832Sgnn int i; 413230832Sgnn 414251129Sdelphij if (strncmp(dev, NFLOG_IFACE, strlen(NFLOG_IFACE)) == 0) { 415251129Sdelphij dev += strlen(NFLOG_IFACE); 416251129Sdelphij type = NFLOG; 417230832Sgnn 418251129Sdelphij } else if (strncmp(dev, NFQUEUE_IFACE, strlen(NFQUEUE_IFACE)) == 0) { 419251129Sdelphij dev += strlen(NFQUEUE_IFACE); 420251129Sdelphij type = NFQUEUE; 421251129Sdelphij } 422251129Sdelphij 423251129Sdelphij if (type != OTHER && *dev == ':') { 424251129Sdelphij dev++; 425230832Sgnn while (*dev) { 426230832Sgnn long int group_id; 427230832Sgnn char *end_dev; 428230832Sgnn 429230832Sgnn if (group_count == 32) { 430230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 431230832Sgnn "Maximum 32 netfilter groups! dev: %s", 432230832Sgnn handle->opt.source); 433230832Sgnn return PCAP_ERROR; 434230832Sgnn } 435230832Sgnn 436230832Sgnn group_id = strtol(dev, &end_dev, 0); 437230832Sgnn if (end_dev != dev) { 438230832Sgnn if (group_id < 0 || group_id > 65535) { 439230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 440230832Sgnn "Netfilter group range from 0 to 65535 (got %ld)", 441230832Sgnn group_id); 442230832Sgnn return PCAP_ERROR; 443230832Sgnn } 444230832Sgnn 445230832Sgnn groups[group_count++] = (unsigned short) group_id; 446230832Sgnn dev = end_dev; 447230832Sgnn } 448230832Sgnn if (*dev != ',') 449230832Sgnn break; 450230832Sgnn dev++; 451230832Sgnn } 452230832Sgnn } 453230832Sgnn 454251129Sdelphij if (type == OTHER || *dev) { 455230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 456230832Sgnn "Can't get netfilter group(s) index from %s", 457230832Sgnn handle->opt.source); 458230832Sgnn return PCAP_ERROR; 459230832Sgnn } 460230832Sgnn 461230832Sgnn /* if no groups, add default: 0 */ 462230832Sgnn if (!group_count) { 463230832Sgnn groups[0] = 0; 464230832Sgnn group_count = 1; 465230832Sgnn } 466230832Sgnn 467230832Sgnn /* Initialize some components of the pcap structure. */ 468230832Sgnn handle->bufsize = 128 + handle->snapshot; 469230832Sgnn handle->offset = 0; 470251129Sdelphij handle->read_op = netfilter_read_linux; 471230832Sgnn handle->inject_op = netfilter_inject_linux; 472230832Sgnn handle->setfilter_op = install_bpf_program; /* no kernel filtering */ 473230832Sgnn handle->setdirection_op = NULL; 474230832Sgnn handle->set_datalink_op = NULL; 475230832Sgnn handle->set_datalink_op = netfilter_set_datalink; 476230832Sgnn handle->getnonblock_op = pcap_getnonblock_fd; 477230832Sgnn handle->setnonblock_op = pcap_setnonblock_fd; 478230832Sgnn handle->stats_op = netfilter_stats_linux; 479230832Sgnn 480230832Sgnn /* Create netlink socket */ 481230832Sgnn handle->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); 482230832Sgnn if (handle->fd < 0) { 483230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't create raw socket %d:%s", errno, pcap_strerror(errno)); 484230832Sgnn return PCAP_ERROR; 485230832Sgnn } 486230832Sgnn 487251129Sdelphij if (type == NFLOG) { 488251129Sdelphij handle->linktype = DLT_NFLOG; 489251129Sdelphij handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); 490251129Sdelphij if (handle->dlt_list != NULL) { 491251129Sdelphij handle->dlt_list[0] = DLT_NFLOG; 492251129Sdelphij handle->dlt_list[1] = DLT_IPV4; 493251129Sdelphij handle->dlt_count = 2; 494251129Sdelphij } 495230832Sgnn 496251129Sdelphij } else 497251129Sdelphij handle->linktype = DLT_IPV4; 498251129Sdelphij 499230832Sgnn handle->buffer = malloc(handle->bufsize); 500230832Sgnn if (!handle->buffer) { 501230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s", pcap_strerror(errno)); 502230832Sgnn goto close_fail; 503230832Sgnn } 504230832Sgnn 505251129Sdelphij if (type == NFLOG) { 506251129Sdelphij if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) { 507251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_UNBIND: %s", pcap_strerror(errno)); 508251129Sdelphij goto close_fail; 509251129Sdelphij } 510230832Sgnn 511251129Sdelphij if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) { 512251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_BIND: %s", pcap_strerror(errno)); 513251129Sdelphij goto close_fail; 514251129Sdelphij } 515230832Sgnn 516251129Sdelphij /* Bind socket to the nflog groups */ 517251129Sdelphij for (i = 0; i < group_count; i++) { 518251129Sdelphij if (nflog_send_config_cmd(handle, groups[i], NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { 519251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't listen on group group index: %s", pcap_strerror(errno)); 520251129Sdelphij goto close_fail; 521251129Sdelphij } 522251129Sdelphij 523251129Sdelphij if (nflog_send_config_mode(handle, groups[i], NFULNL_COPY_PACKET, handle->snapshot) < 0) { 524251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_COPY_PACKET: %s", pcap_strerror(errno)); 525251129Sdelphij goto close_fail; 526251129Sdelphij } 527251129Sdelphij } 528251129Sdelphij 529251129Sdelphij } else { 530251129Sdelphij if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) { 531251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_CFG_CMD_PF_UNBIND: %s", pcap_strerror(errno)); 532230832Sgnn goto close_fail; 533230832Sgnn } 534230832Sgnn 535251129Sdelphij if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_BIND, AF_INET) < 0) { 536251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_CFG_CMD_PF_BIND: %s", pcap_strerror(errno)); 537230832Sgnn goto close_fail; 538230832Sgnn } 539251129Sdelphij 540251129Sdelphij /* Bind socket to the nfqueue groups */ 541251129Sdelphij for (i = 0; i < group_count; i++) { 542251129Sdelphij if (nfqueue_send_config_cmd(handle, groups[i], NFQNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { 543251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't listen on group group index: %s", pcap_strerror(errno)); 544251129Sdelphij goto close_fail; 545251129Sdelphij } 546251129Sdelphij 547251129Sdelphij if (nfqueue_send_config_mode(handle, groups[i], NFQNL_COPY_PACKET, handle->snapshot) < 0) { 548251129Sdelphij snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_COPY_PACKET: %s", pcap_strerror(errno)); 549251129Sdelphij goto close_fail; 550251129Sdelphij } 551251129Sdelphij } 552230832Sgnn } 553230832Sgnn 554230832Sgnn if (handle->opt.rfmon) { 555230832Sgnn /* 556230832Sgnn * Monitor mode doesn't apply to netfilter devices. 557230832Sgnn */ 558230832Sgnn pcap_cleanup_live_common(handle); 559230832Sgnn return PCAP_ERROR_RFMON_NOTSUP; 560230832Sgnn } 561230832Sgnn 562230832Sgnn if (handle->opt.buffer_size != 0) { 563230832Sgnn /* 564230832Sgnn * Set the socket buffer size to the specified value. 565230832Sgnn */ 566230832Sgnn if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF, &handle->opt.buffer_size, sizeof(handle->opt.buffer_size)) == -1) { 567230832Sgnn snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SO_RCVBUF: %s", pcap_strerror(errno)); 568230832Sgnn goto close_fail; 569230832Sgnn } 570230832Sgnn } 571230832Sgnn 572230832Sgnn handle->selectable_fd = handle->fd; 573230832Sgnn return 0; 574230832Sgnn 575230832Sgnnclose_fail: 576230832Sgnn pcap_cleanup_live_common(handle); 577230832Sgnn return PCAP_ERROR; 578230832Sgnn} 579230832Sgnn 580230832Sgnnpcap_t * 581251129Sdelphijnetfilter_create(const char *device, char *ebuf, int *is_ours) 582230832Sgnn{ 583251129Sdelphij const char *cp; 584230832Sgnn pcap_t *p; 585230832Sgnn 586251129Sdelphij /* Does this look like an netfilter device? */ 587251129Sdelphij cp = strrchr(device, '/'); 588251129Sdelphij if (cp == NULL) 589251129Sdelphij cp = device; 590251129Sdelphij 591251129Sdelphij /* Does it begin with NFLOG_IFACE or NFQUEUE_IFACE? */ 592251129Sdelphij if (strncmp(cp, NFLOG_IFACE, sizeof NFLOG_IFACE - 1) == 0) 593251129Sdelphij cp += sizeof NFLOG_IFACE - 1; 594251129Sdelphij else if (strncmp(cp, NFQUEUE_IFACE, sizeof NFQUEUE_IFACE - 1) == 0) 595251129Sdelphij cp += sizeof NFQUEUE_IFACE - 1; 596251129Sdelphij else { 597251129Sdelphij /* Nope, doesn't begin with NFLOG_IFACE nor NFQUEUE_IFACE */ 598251129Sdelphij *is_ours = 0; 599251129Sdelphij return NULL; 600251129Sdelphij } 601251129Sdelphij 602251129Sdelphij /* 603251129Sdelphij * Yes - is that either the end of the name, or is it followed 604251129Sdelphij * by a colon? 605251129Sdelphij */ 606251129Sdelphij if (*cp != ':' && *cp != '\0') { 607251129Sdelphij /* Nope */ 608251129Sdelphij *is_ours = 0; 609251129Sdelphij return NULL; 610251129Sdelphij } 611251129Sdelphij 612251129Sdelphij /* OK, it's probably ours. */ 613251129Sdelphij *is_ours = 1; 614251129Sdelphij 615230832Sgnn p = pcap_create_common(device, ebuf); 616230832Sgnn if (p == NULL) 617230832Sgnn return (NULL); 618230832Sgnn 619251129Sdelphij p->activate_op = netfilter_activate; 620230832Sgnn return (p); 621230832Sgnn} 622230832Sgnn 623230832Sgnnint 624251129Sdelphijnetfilter_findalldevs(pcap_if_t **alldevsp, char *err_str) 625230832Sgnn{ 626230832Sgnn pcap_if_t *found_dev = *alldevsp; 627230832Sgnn int sock; 628230832Sgnn 629230832Sgnn sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); 630230832Sgnn if (sock < 0) { 631241231Sdelphij /* if netlink is not supported this is not fatal */ 632241231Sdelphij if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) 633230832Sgnn return 0; 634230832Sgnn snprintf(err_str, PCAP_ERRBUF_SIZE, "Can't open netlink socket %d:%s", 635230832Sgnn errno, pcap_strerror(errno)); 636230832Sgnn return -1; 637230832Sgnn } 638230832Sgnn close(sock); 639230832Sgnn 640230832Sgnn if (pcap_add_if(&found_dev, NFLOG_IFACE, 0, "Linux netfilter log (NFLOG) interface", err_str) < 0) 641230832Sgnn return -1; 642251129Sdelphij if (pcap_add_if(&found_dev, NFQUEUE_IFACE, 0, "Linux netfilter queue (NFQUEUE) interface", err_str) < 0) 643251129Sdelphij return -1; 644230832Sgnn return 0; 645230832Sgnn} 646