1335640Shselasky/* 2335640Shselasky * Copyright (c) 2011 Jakub Zawadzki 3335640Shselasky * All rights reserved. 4335640Shselasky * 5335640Shselasky * Redistribution and use in source and binary forms, with or without 6335640Shselasky * modification, are permitted provided that the following conditions 7335640Shselasky * are met: 8335640Shselasky * 9335640Shselasky * 1. Redistributions of source code must retain the above copyright 10335640Shselasky * notice, this list of conditions and the following disclaimer. 11335640Shselasky * 2. Redistributions in binary form must reproduce the above copyright 12335640Shselasky * notice, this list of conditions and the following disclaimer in the 13335640Shselasky * documentation and/or other materials provided with the distribution. 14335640Shselasky * 3. The name of the author may not be used to endorse or promote 15335640Shselasky * products derived from this software without specific prior written 16335640Shselasky * permission. 17335640Shselasky * 18335640Shselasky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19335640Shselasky * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20335640Shselasky * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21335640Shselasky * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22335640Shselasky * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23335640Shselasky * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24335640Shselasky * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25335640Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26335640Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27335640Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28335640Shselasky * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29335640Shselasky */ 30335640Shselasky 31335640Shselasky#ifdef HAVE_CONFIG_H 32335640Shselasky#include <config.h> 33335640Shselasky#endif 34335640Shselasky 35335640Shselasky#include "pcap-int.h" 36335640Shselasky 37335640Shselasky#ifdef NEED_STRERROR_H 38335640Shselasky#include "strerror.h" 39335640Shselasky#endif 40335640Shselasky 41335640Shselasky#include <errno.h> 42335640Shselasky#include <stdlib.h> 43335640Shselasky#include <unistd.h> 44335640Shselasky#include <string.h> 45335640Shselasky#include <sys/socket.h> 46335640Shselasky#include <arpa/inet.h> 47335640Shselasky 48335640Shselasky#include <time.h> 49335640Shselasky#include <sys/time.h> 50335640Shselasky#include <netinet/in.h> 51335640Shselasky#include <linux/types.h> 52335640Shselasky 53335640Shselasky#include <linux/netlink.h> 54335640Shselasky#include <linux/netfilter.h> 55335640Shselasky#include <linux/netfilter/nfnetlink.h> 56335640Shselasky#include <linux/netfilter/nfnetlink_log.h> 57335640Shselasky#include <linux/netfilter/nfnetlink_queue.h> 58335640Shselasky 59335640Shselasky/* NOTE: if your program drops privilages after pcap_activate() it WON'T work with nfqueue. 60335640Shselasky * It took me quite some time to debug ;/ 61335640Shselasky * 62335640Shselasky * Sending any data to nfnetlink socket requires CAP_NET_ADMIN privilages, 63335640Shselasky * and in nfqueue we need to send verdict reply after recving packet. 64335640Shselasky * 65335640Shselasky * In tcpdump you can disable dropping privilages with -Z root 66335640Shselasky */ 67335640Shselasky 68335640Shselasky#include "pcap-netfilter-linux.h" 69335640Shselasky 70335640Shselasky#define HDR_LENGTH (NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct nfgenmsg)))) 71335640Shselasky 72335640Shselasky#define NFLOG_IFACE "nflog" 73335640Shselasky#define NFQUEUE_IFACE "nfqueue" 74335640Shselasky 75335640Shselaskytypedef enum { OTHER = -1, NFLOG, NFQUEUE } nftype_t; 76335640Shselasky 77335640Shselasky/* 78335640Shselasky * Private data for capturing on Linux netfilter sockets. 79335640Shselasky */ 80335640Shselaskystruct pcap_netfilter { 81335640Shselasky u_int packets_read; /* count of packets read with recvfrom() */ 82335640Shselasky u_int packets_nobufs; /* ENOBUFS counter */ 83335640Shselasky}; 84335640Shselasky 85335640Shselaskystatic int nfqueue_send_verdict(const pcap_t *handle, uint16_t group_id, u_int32_t id, u_int32_t verdict); 86335640Shselasky 87335640Shselasky 88335640Shselaskystatic int 89335640Shselaskynetfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) 90335640Shselasky{ 91335640Shselasky struct pcap_netfilter *handlep = handle->priv; 92335640Shselasky register u_char *bp, *ep; 93335640Shselasky int count = 0; 94335640Shselasky int len; 95335640Shselasky 96335640Shselasky /* 97335640Shselasky * Has "pcap_breakloop()" been called? 98335640Shselasky */ 99335640Shselasky if (handle->break_loop) { 100335640Shselasky /* 101335640Shselasky * Yes - clear the flag that indicates that it 102335640Shselasky * has, and return PCAP_ERROR_BREAK to indicate 103335640Shselasky * that we were told to break out of the loop. 104335640Shselasky */ 105335640Shselasky handle->break_loop = 0; 106335640Shselasky return PCAP_ERROR_BREAK; 107335640Shselasky } 108335640Shselasky len = handle->cc; 109335640Shselasky if (len == 0) { 110335640Shselasky /* 111335640Shselasky * The buffer is empty; refill it. 112335640Shselasky * 113335640Shselasky * We ignore EINTR, as that might just be due to a signal 114335640Shselasky * being delivered - if the signal should interrupt the 115335640Shselasky * loop, the signal handler should call pcap_breakloop() 116335640Shselasky * to set handle->break_loop (we ignore it on other 117335640Shselasky * platforms as well). 118335640Shselasky */ 119335640Shselasky do { 120335640Shselasky len = recv(handle->fd, handle->buffer, handle->bufsize, 0); 121335640Shselasky if (handle->break_loop) { 122335640Shselasky handle->break_loop = 0; 123335640Shselasky return PCAP_ERROR_BREAK; 124335640Shselasky } 125335640Shselasky if (errno == ENOBUFS) 126335640Shselasky handlep->packets_nobufs++; 127335640Shselasky } while ((len == -1) && (errno == EINTR || errno == ENOBUFS)); 128335640Shselasky 129335640Shselasky if (len < 0) { 130335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 131335640Shselasky PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); 132335640Shselasky return PCAP_ERROR; 133335640Shselasky } 134335640Shselasky 135335640Shselasky bp = (unsigned char *)handle->buffer; 136335640Shselasky } else 137335640Shselasky bp = handle->bp; 138335640Shselasky ep = bp + len; 139335640Shselasky while (bp < ep) { 140335640Shselasky const struct nlmsghdr *nlh = (const struct nlmsghdr *) bp; 141335640Shselasky uint32_t msg_len; 142335640Shselasky nftype_t type = OTHER; 143335640Shselasky /* 144335640Shselasky * Has "pcap_breakloop()" been called? 145335640Shselasky * If so, return immediately - if we haven't read any 146335640Shselasky * packets, clear the flag and return PCAP_ERROR_BREAK 147335640Shselasky * to indicate that we were told to break out of the loop, 148335640Shselasky * otherwise leave the flag set, so that the *next* call 149335640Shselasky * will break out of the loop without having read any 150335640Shselasky * packets, and return the number of packets we've 151335640Shselasky * processed so far. 152335640Shselasky */ 153335640Shselasky if (handle->break_loop) { 154335640Shselasky handle->bp = bp; 155335640Shselasky handle->cc = ep - bp; 156335640Shselasky if (count == 0) { 157335640Shselasky handle->break_loop = 0; 158335640Shselasky return PCAP_ERROR_BREAK; 159335640Shselasky } else 160335640Shselasky return count; 161335640Shselasky } 162335640Shselasky if (ep - bp < NLMSG_SPACE(0)) { 163335640Shselasky /* 164335640Shselasky * There's less than one netlink message left 165335640Shselasky * in the buffer. Give up. 166335640Shselasky */ 167335640Shselasky break; 168335640Shselasky } 169335640Shselasky 170335640Shselasky if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || (u_int)len < nlh->nlmsg_len) { 171335640Shselasky pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len); 172335640Shselasky return -1; 173335640Shselasky } 174335640Shselasky 175335640Shselasky if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && 176335640Shselasky NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) 177335640Shselasky type = NFLOG; 178335640Shselasky else if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && 179335640Shselasky NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET) 180335640Shselasky type = NFQUEUE; 181335640Shselasky 182335640Shselasky if (type != OTHER) { 183335640Shselasky const unsigned char *payload = NULL; 184335640Shselasky struct pcap_pkthdr pkth; 185335640Shselasky 186335640Shselasky const struct nfgenmsg *nfg = NULL; 187335640Shselasky int id = 0; 188335640Shselasky 189335640Shselasky if (handle->linktype != DLT_NFLOG) { 190335640Shselasky const struct nfattr *payload_attr = NULL; 191335640Shselasky 192335640Shselasky if (nlh->nlmsg_len < HDR_LENGTH) { 193335640Shselasky pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); 194335640Shselasky return -1; 195335640Shselasky } 196335640Shselasky 197335640Shselasky nfg = NLMSG_DATA(nlh); 198335640Shselasky if (nlh->nlmsg_len > HDR_LENGTH) { 199335640Shselasky struct nfattr *attr = NFM_NFA(nfg); 200335640Shselasky int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); 201335640Shselasky 202335640Shselasky while (NFA_OK(attr, attr_len)) { 203335640Shselasky if (type == NFQUEUE) { 204335640Shselasky switch (NFA_TYPE(attr)) { 205335640Shselasky case NFQA_PACKET_HDR: 206335640Shselasky { 207335640Shselasky const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr); 208335640Shselasky 209335640Shselasky id = ntohl(pkt_hdr->packet_id); 210335640Shselasky break; 211335640Shselasky } 212335640Shselasky case NFQA_PAYLOAD: 213335640Shselasky payload_attr = attr; 214335640Shselasky break; 215335640Shselasky } 216335640Shselasky 217335640Shselasky } else if (type == NFLOG) { 218335640Shselasky switch (NFA_TYPE(attr)) { 219335640Shselasky case NFULA_PAYLOAD: 220335640Shselasky payload_attr = attr; 221335640Shselasky break; 222335640Shselasky } 223335640Shselasky } 224335640Shselasky attr = NFA_NEXT(attr, attr_len); 225335640Shselasky } 226335640Shselasky } 227335640Shselasky 228335640Shselasky if (payload_attr) { 229335640Shselasky payload = NFA_DATA(payload_attr); 230335640Shselasky pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); 231335640Shselasky } 232335640Shselasky 233335640Shselasky } else { 234335640Shselasky payload = NLMSG_DATA(nlh); 235335640Shselasky pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); 236335640Shselasky } 237335640Shselasky 238335640Shselasky if (payload) { 239335640Shselasky /* pkth.caplen = min (payload_len, handle->snapshot); */ 240335640Shselasky 241335640Shselasky gettimeofday(&pkth.ts, NULL); 242335640Shselasky if (handle->fcode.bf_insns == NULL || 243335640Shselasky bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) 244335640Shselasky { 245335640Shselasky handlep->packets_read++; 246335640Shselasky callback(user, &pkth, payload); 247335640Shselasky count++; 248335640Shselasky } 249335640Shselasky } 250335640Shselasky 251335640Shselasky if (type == NFQUEUE) { 252335640Shselasky /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */ 253335640Shselasky /* if type == NFQUEUE, handle->linktype is always != DLT_NFLOG, 254335640Shselasky so nfg is always initialized to NLMSG_DATA(nlh). */ 255335640Shselasky if (nfg != NULL) 256335640Shselasky nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT); 257335640Shselasky } 258335640Shselasky } 259335640Shselasky 260335640Shselasky msg_len = NLMSG_ALIGN(nlh->nlmsg_len); 261335640Shselasky /* 262335640Shselasky * If the message length would run past the end of the 263335640Shselasky * buffer, truncate it to the remaining space in the 264335640Shselasky * buffer. 265335640Shselasky */ 266335640Shselasky if (msg_len > ep - bp) 267335640Shselasky msg_len = ep - bp; 268335640Shselasky 269335640Shselasky bp += msg_len; 270335640Shselasky if (count >= max_packets && !PACKET_COUNT_IS_UNLIMITED(max_packets)) { 271335640Shselasky handle->bp = bp; 272335640Shselasky handle->cc = ep - bp; 273335640Shselasky if (handle->cc < 0) 274335640Shselasky handle->cc = 0; 275335640Shselasky return count; 276335640Shselasky } 277335640Shselasky } 278335640Shselasky 279335640Shselasky handle->cc = 0; 280335640Shselasky return count; 281335640Shselasky} 282335640Shselasky 283335640Shselaskystatic int 284335640Shselaskynetfilter_set_datalink(pcap_t *handle, int dlt) 285335640Shselasky{ 286335640Shselasky handle->linktype = dlt; 287335640Shselasky return 0; 288335640Shselasky} 289335640Shselasky 290335640Shselaskystatic int 291335640Shselaskynetfilter_stats_linux(pcap_t *handle, struct pcap_stat *stats) 292335640Shselasky{ 293335640Shselasky struct pcap_netfilter *handlep = handle->priv; 294335640Shselasky 295335640Shselasky stats->ps_recv = handlep->packets_read; 296335640Shselasky stats->ps_drop = handlep->packets_nobufs; 297335640Shselasky stats->ps_ifdrop = 0; 298335640Shselasky return 0; 299335640Shselasky} 300335640Shselasky 301335640Shselaskystatic int 302335640Shselaskynetfilter_inject_linux(pcap_t *handle, const void *buf _U_, size_t size _U_) 303335640Shselasky{ 304356341Scy pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 305356341Scy "Packet injection is not supported on netfilter devices"); 306335640Shselasky return (-1); 307335640Shselasky} 308335640Shselasky 309335640Shselaskystruct my_nfattr { 310335640Shselasky uint16_t nfa_len; 311335640Shselasky uint16_t nfa_type; 312335640Shselasky void *data; 313335640Shselasky}; 314335640Shselasky 315335640Shselaskystatic int 316335640Shselaskynetfilter_send_config_msg(const pcap_t *handle, uint16_t msg_type, int ack, u_int8_t family, u_int16_t res_id, const struct my_nfattr *mynfa) 317335640Shselasky{ 318335640Shselasky char buf[1024] __attribute__ ((aligned)); 319356341Scy memset(buf, 0, sizeof(buf)); 320335640Shselasky 321335640Shselasky struct nlmsghdr *nlh = (struct nlmsghdr *) buf; 322335640Shselasky struct nfgenmsg *nfg = (struct nfgenmsg *) (buf + sizeof(struct nlmsghdr)); 323335640Shselasky 324335640Shselasky struct sockaddr_nl snl; 325335640Shselasky static unsigned int seq_id; 326335640Shselasky 327335640Shselasky if (!seq_id) 328335640Shselasky seq_id = time(NULL); 329335640Shselasky ++seq_id; 330335640Shselasky 331335640Shselasky nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); 332335640Shselasky nlh->nlmsg_type = msg_type; 333335640Shselasky nlh->nlmsg_flags = NLM_F_REQUEST | (ack ? NLM_F_ACK : 0); 334335640Shselasky nlh->nlmsg_pid = 0; /* to kernel */ 335335640Shselasky nlh->nlmsg_seq = seq_id; 336335640Shselasky 337335640Shselasky nfg->nfgen_family = family; 338335640Shselasky nfg->version = NFNETLINK_V0; 339335640Shselasky nfg->res_id = htons(res_id); 340335640Shselasky 341335640Shselasky if (mynfa) { 342335640Shselasky struct nfattr *nfa = (struct nfattr *) (buf + NLMSG_ALIGN(nlh->nlmsg_len)); 343335640Shselasky 344335640Shselasky nfa->nfa_type = mynfa->nfa_type; 345335640Shselasky nfa->nfa_len = NFA_LENGTH(mynfa->nfa_len); 346335640Shselasky memcpy(NFA_DATA(nfa), mynfa->data, mynfa->nfa_len); 347335640Shselasky nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NFA_ALIGN(nfa->nfa_len); 348335640Shselasky } 349335640Shselasky 350335640Shselasky memset(&snl, 0, sizeof(snl)); 351335640Shselasky snl.nl_family = AF_NETLINK; 352335640Shselasky 353335640Shselasky if (sendto(handle->fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *) &snl, sizeof(snl)) == -1) 354335640Shselasky return -1; 355335640Shselasky 356335640Shselasky if (!ack) 357335640Shselasky return 0; 358335640Shselasky 359335640Shselasky /* waiting for reply loop */ 360335640Shselasky do { 361335640Shselasky socklen_t addrlen = sizeof(snl); 362335640Shselasky int len; 363335640Shselasky 364335640Shselasky /* ignore interrupt system call error */ 365335640Shselasky do { 366335640Shselasky len = recvfrom(handle->fd, buf, sizeof(buf), 0, (struct sockaddr *) &snl, &addrlen); 367335640Shselasky } while ((len == -1) && (errno == EINTR)); 368335640Shselasky 369335640Shselasky if (len <= 0) 370335640Shselasky return len; 371335640Shselasky 372335640Shselasky if (addrlen != sizeof(snl) || snl.nl_family != AF_NETLINK) { 373335640Shselasky errno = EINVAL; 374335640Shselasky return -1; 375335640Shselasky } 376335640Shselasky 377335640Shselasky nlh = (struct nlmsghdr *) buf; 378335640Shselasky if (snl.nl_pid != 0 || seq_id != nlh->nlmsg_seq) /* if not from kernel or wrong sequence skip */ 379335640Shselasky continue; 380335640Shselasky 381335640Shselasky while ((u_int)len >= NLMSG_SPACE(0) && NLMSG_OK(nlh, (u_int)len)) { 382335640Shselasky if (nlh->nlmsg_type == NLMSG_ERROR || (nlh->nlmsg_type == NLMSG_DONE && nlh->nlmsg_flags & NLM_F_MULTI)) { 383335640Shselasky if (nlh->nlmsg_len < NLMSG_ALIGN(sizeof(struct nlmsgerr))) { 384335640Shselasky errno = EBADMSG; 385335640Shselasky return -1; 386335640Shselasky } 387335640Shselasky errno = -(*((int *)NLMSG_DATA(nlh))); 388335640Shselasky return (errno == 0) ? 0 : -1; 389335640Shselasky } 390335640Shselasky nlh = NLMSG_NEXT(nlh, len); 391335640Shselasky } 392335640Shselasky } while (1); 393335640Shselasky 394335640Shselasky return -1; /* never here */ 395335640Shselasky} 396335640Shselasky 397335640Shselaskystatic int 398335640Shselaskynflog_send_config_msg(const pcap_t *handle, uint8_t family, u_int16_t group_id, const struct my_nfattr *mynfa) 399335640Shselasky{ 400335640Shselasky return netfilter_send_config_msg(handle, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, 1, family, group_id, mynfa); 401335640Shselasky} 402335640Shselasky 403335640Shselaskystatic int 404335640Shselaskynflog_send_config_cmd(const pcap_t *handle, uint16_t group_id, u_int8_t cmd, u_int8_t family) 405335640Shselasky{ 406335640Shselasky struct nfulnl_msg_config_cmd msg; 407335640Shselasky struct my_nfattr nfa; 408335640Shselasky 409335640Shselasky msg.command = cmd; 410335640Shselasky 411335640Shselasky nfa.data = &msg; 412335640Shselasky nfa.nfa_type = NFULA_CFG_CMD; 413335640Shselasky nfa.nfa_len = sizeof(msg); 414335640Shselasky 415335640Shselasky return nflog_send_config_msg(handle, family, group_id, &nfa); 416335640Shselasky} 417335640Shselasky 418335640Shselaskystatic int 419335640Shselaskynflog_send_config_mode(const pcap_t *handle, uint16_t group_id, u_int8_t copy_mode, u_int32_t copy_range) 420335640Shselasky{ 421335640Shselasky struct nfulnl_msg_config_mode msg; 422335640Shselasky struct my_nfattr nfa; 423335640Shselasky 424335640Shselasky msg.copy_range = htonl(copy_range); 425335640Shselasky msg.copy_mode = copy_mode; 426335640Shselasky 427335640Shselasky nfa.data = &msg; 428335640Shselasky nfa.nfa_type = NFULA_CFG_MODE; 429335640Shselasky nfa.nfa_len = sizeof(msg); 430335640Shselasky 431335640Shselasky return nflog_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 432335640Shselasky} 433335640Shselasky 434335640Shselaskystatic int 435335640Shselaskynfqueue_send_verdict(const pcap_t *handle, uint16_t group_id, u_int32_t id, u_int32_t verdict) 436335640Shselasky{ 437335640Shselasky struct nfqnl_msg_verdict_hdr msg; 438335640Shselasky struct my_nfattr nfa; 439335640Shselasky 440335640Shselasky msg.id = htonl(id); 441335640Shselasky msg.verdict = htonl(verdict); 442335640Shselasky 443335640Shselasky nfa.data = &msg; 444335640Shselasky nfa.nfa_type = NFQA_VERDICT_HDR; 445335640Shselasky nfa.nfa_len = sizeof(msg); 446335640Shselasky 447335640Shselasky return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT, 0, AF_UNSPEC, group_id, &nfa); 448335640Shselasky} 449335640Shselasky 450335640Shselaskystatic int 451335640Shselaskynfqueue_send_config_msg(const pcap_t *handle, uint8_t family, u_int16_t group_id, const struct my_nfattr *mynfa) 452335640Shselasky{ 453335640Shselasky return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG, 1, family, group_id, mynfa); 454335640Shselasky} 455335640Shselasky 456335640Shselaskystatic int 457335640Shselaskynfqueue_send_config_cmd(const pcap_t *handle, uint16_t group_id, u_int8_t cmd, u_int16_t pf) 458335640Shselasky{ 459335640Shselasky struct nfqnl_msg_config_cmd msg; 460335640Shselasky struct my_nfattr nfa; 461335640Shselasky 462335640Shselasky msg.command = cmd; 463335640Shselasky msg.pf = htons(pf); 464335640Shselasky 465335640Shselasky nfa.data = &msg; 466335640Shselasky nfa.nfa_type = NFQA_CFG_CMD; 467335640Shselasky nfa.nfa_len = sizeof(msg); 468335640Shselasky 469335640Shselasky return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 470335640Shselasky} 471335640Shselasky 472335640Shselaskystatic int 473335640Shselaskynfqueue_send_config_mode(const pcap_t *handle, uint16_t group_id, u_int8_t copy_mode, u_int32_t copy_range) 474335640Shselasky{ 475335640Shselasky struct nfqnl_msg_config_params msg; 476335640Shselasky struct my_nfattr nfa; 477335640Shselasky 478335640Shselasky msg.copy_range = htonl(copy_range); 479335640Shselasky msg.copy_mode = copy_mode; 480335640Shselasky 481335640Shselasky nfa.data = &msg; 482335640Shselasky nfa.nfa_type = NFQA_CFG_PARAMS; 483335640Shselasky nfa.nfa_len = sizeof(msg); 484335640Shselasky 485335640Shselasky return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa); 486335640Shselasky} 487335640Shselasky 488335640Shselaskystatic int 489335640Shselaskynetfilter_activate(pcap_t* handle) 490335640Shselasky{ 491335640Shselasky const char *dev = handle->opt.device; 492335640Shselasky unsigned short groups[32]; 493335640Shselasky int group_count = 0; 494335640Shselasky nftype_t type = OTHER; 495335640Shselasky int i; 496335640Shselasky 497335640Shselasky if (strncmp(dev, NFLOG_IFACE, strlen(NFLOG_IFACE)) == 0) { 498335640Shselasky dev += strlen(NFLOG_IFACE); 499335640Shselasky type = NFLOG; 500335640Shselasky 501335640Shselasky } else if (strncmp(dev, NFQUEUE_IFACE, strlen(NFQUEUE_IFACE)) == 0) { 502335640Shselasky dev += strlen(NFQUEUE_IFACE); 503335640Shselasky type = NFQUEUE; 504335640Shselasky } 505335640Shselasky 506335640Shselasky if (type != OTHER && *dev == ':') { 507335640Shselasky dev++; 508335640Shselasky while (*dev) { 509335640Shselasky long int group_id; 510335640Shselasky char *end_dev; 511335640Shselasky 512335640Shselasky if (group_count == 32) { 513335640Shselasky pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 514335640Shselasky "Maximum 32 netfilter groups! dev: %s", 515335640Shselasky handle->opt.device); 516335640Shselasky return PCAP_ERROR; 517335640Shselasky } 518335640Shselasky 519335640Shselasky group_id = strtol(dev, &end_dev, 0); 520335640Shselasky if (end_dev != dev) { 521335640Shselasky if (group_id < 0 || group_id > 65535) { 522335640Shselasky pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 523335640Shselasky "Netfilter group range from 0 to 65535 (got %ld)", 524335640Shselasky group_id); 525335640Shselasky return PCAP_ERROR; 526335640Shselasky } 527335640Shselasky 528335640Shselasky groups[group_count++] = (unsigned short) group_id; 529335640Shselasky dev = end_dev; 530335640Shselasky } 531335640Shselasky if (*dev != ',') 532335640Shselasky break; 533335640Shselasky dev++; 534335640Shselasky } 535335640Shselasky } 536335640Shselasky 537335640Shselasky if (type == OTHER || *dev) { 538335640Shselasky pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 539335640Shselasky "Can't get netfilter group(s) index from %s", 540335640Shselasky handle->opt.device); 541335640Shselasky return PCAP_ERROR; 542335640Shselasky } 543335640Shselasky 544335640Shselasky /* if no groups, add default: 0 */ 545335640Shselasky if (!group_count) { 546335640Shselasky groups[0] = 0; 547335640Shselasky group_count = 1; 548335640Shselasky } 549335640Shselasky 550335640Shselasky /* 551335640Shselasky * Turn a negative snapshot value (invalid), a snapshot value of 552335640Shselasky * 0 (unspecified), or a value bigger than the normal maximum 553335640Shselasky * value, into the maximum allowed value. 554335640Shselasky * 555335640Shselasky * If some application really *needs* a bigger snapshot 556335640Shselasky * length, we should just increase MAXIMUM_SNAPLEN. 557335640Shselasky */ 558335640Shselasky if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) 559335640Shselasky handle->snapshot = MAXIMUM_SNAPLEN; 560335640Shselasky 561335640Shselasky /* Initialize some components of the pcap structure. */ 562335640Shselasky handle->bufsize = 128 + handle->snapshot; 563335640Shselasky handle->offset = 0; 564335640Shselasky handle->read_op = netfilter_read_linux; 565335640Shselasky handle->inject_op = netfilter_inject_linux; 566335640Shselasky handle->setfilter_op = install_bpf_program; /* no kernel filtering */ 567335640Shselasky handle->setdirection_op = NULL; 568335640Shselasky handle->set_datalink_op = netfilter_set_datalink; 569335640Shselasky handle->getnonblock_op = pcap_getnonblock_fd; 570335640Shselasky handle->setnonblock_op = pcap_setnonblock_fd; 571335640Shselasky handle->stats_op = netfilter_stats_linux; 572335640Shselasky 573335640Shselasky /* Create netlink socket */ 574335640Shselasky handle->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); 575335640Shselasky if (handle->fd < 0) { 576335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, 577335640Shselasky errno, "Can't create raw socket"); 578335640Shselasky return PCAP_ERROR; 579335640Shselasky } 580335640Shselasky 581335640Shselasky if (type == NFLOG) { 582335640Shselasky handle->linktype = DLT_NFLOG; 583335640Shselasky handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); 584335640Shselasky if (handle->dlt_list != NULL) { 585335640Shselasky handle->dlt_list[0] = DLT_NFLOG; 586335640Shselasky handle->dlt_list[1] = DLT_IPV4; 587335640Shselasky handle->dlt_count = 2; 588335640Shselasky } 589335640Shselasky 590335640Shselasky } else 591335640Shselasky handle->linktype = DLT_IPV4; 592335640Shselasky 593335640Shselasky handle->buffer = malloc(handle->bufsize); 594335640Shselasky if (!handle->buffer) { 595335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, 596335640Shselasky errno, "Can't allocate dump buffer"); 597335640Shselasky goto close_fail; 598335640Shselasky } 599335640Shselasky 600335640Shselasky if (type == NFLOG) { 601335640Shselasky if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) { 602335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 603335640Shselasky PCAP_ERRBUF_SIZE, errno, 604335640Shselasky "NFULNL_CFG_CMD_PF_UNBIND"); 605335640Shselasky goto close_fail; 606335640Shselasky } 607335640Shselasky 608335640Shselasky if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) { 609335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 610335640Shselasky PCAP_ERRBUF_SIZE, errno, "NFULNL_CFG_CMD_PF_BIND"); 611335640Shselasky goto close_fail; 612335640Shselasky } 613335640Shselasky 614335640Shselasky /* Bind socket to the nflog groups */ 615335640Shselasky for (i = 0; i < group_count; i++) { 616335640Shselasky if (nflog_send_config_cmd(handle, groups[i], NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { 617335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 618335640Shselasky PCAP_ERRBUF_SIZE, errno, 619335640Shselasky "Can't listen on group group index"); 620335640Shselasky goto close_fail; 621335640Shselasky } 622335640Shselasky 623335640Shselasky if (nflog_send_config_mode(handle, groups[i], NFULNL_COPY_PACKET, handle->snapshot) < 0) { 624335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 625335640Shselasky PCAP_ERRBUF_SIZE, errno, 626335640Shselasky "NFULNL_COPY_PACKET"); 627335640Shselasky goto close_fail; 628335640Shselasky } 629335640Shselasky } 630335640Shselasky 631335640Shselasky } else { 632335640Shselasky if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) { 633335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 634335640Shselasky PCAP_ERRBUF_SIZE, errno, "NFQNL_CFG_CMD_PF_UNBIND"); 635335640Shselasky goto close_fail; 636335640Shselasky } 637335640Shselasky 638335640Shselasky if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_BIND, AF_INET) < 0) { 639335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 640335640Shselasky PCAP_ERRBUF_SIZE, errno, "NFQNL_CFG_CMD_PF_BIND"); 641335640Shselasky goto close_fail; 642335640Shselasky } 643335640Shselasky 644335640Shselasky /* Bind socket to the nfqueue groups */ 645335640Shselasky for (i = 0; i < group_count; i++) { 646335640Shselasky if (nfqueue_send_config_cmd(handle, groups[i], NFQNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { 647335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 648335640Shselasky PCAP_ERRBUF_SIZE, errno, 649335640Shselasky "Can't listen on group group index"); 650335640Shselasky goto close_fail; 651335640Shselasky } 652335640Shselasky 653335640Shselasky if (nfqueue_send_config_mode(handle, groups[i], NFQNL_COPY_PACKET, handle->snapshot) < 0) { 654335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 655335640Shselasky PCAP_ERRBUF_SIZE, errno, 656335640Shselasky "NFQNL_COPY_PACKET"); 657335640Shselasky goto close_fail; 658335640Shselasky } 659335640Shselasky } 660335640Shselasky } 661335640Shselasky 662335640Shselasky if (handle->opt.rfmon) { 663335640Shselasky /* 664335640Shselasky * Monitor mode doesn't apply to netfilter devices. 665335640Shselasky */ 666335640Shselasky pcap_cleanup_live_common(handle); 667335640Shselasky return PCAP_ERROR_RFMON_NOTSUP; 668335640Shselasky } 669335640Shselasky 670335640Shselasky if (handle->opt.buffer_size != 0) { 671335640Shselasky /* 672335640Shselasky * Set the socket buffer size to the specified value. 673335640Shselasky */ 674335640Shselasky if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF, &handle->opt.buffer_size, sizeof(handle->opt.buffer_size)) == -1) { 675335640Shselasky pcap_fmt_errmsg_for_errno(handle->errbuf, 676335640Shselasky PCAP_ERRBUF_SIZE, errno, "SO_RCVBUF"); 677335640Shselasky goto close_fail; 678335640Shselasky } 679335640Shselasky } 680335640Shselasky 681335640Shselasky handle->selectable_fd = handle->fd; 682335640Shselasky return 0; 683335640Shselasky 684335640Shselaskyclose_fail: 685335640Shselasky pcap_cleanup_live_common(handle); 686335640Shselasky return PCAP_ERROR; 687335640Shselasky} 688335640Shselasky 689335640Shselaskypcap_t * 690335640Shselaskynetfilter_create(const char *device, char *ebuf, int *is_ours) 691335640Shselasky{ 692335640Shselasky const char *cp; 693335640Shselasky pcap_t *p; 694335640Shselasky 695335640Shselasky /* Does this look like an netfilter device? */ 696335640Shselasky cp = strrchr(device, '/'); 697335640Shselasky if (cp == NULL) 698335640Shselasky cp = device; 699335640Shselasky 700335640Shselasky /* Does it begin with NFLOG_IFACE or NFQUEUE_IFACE? */ 701335640Shselasky if (strncmp(cp, NFLOG_IFACE, sizeof NFLOG_IFACE - 1) == 0) 702335640Shselasky cp += sizeof NFLOG_IFACE - 1; 703335640Shselasky else if (strncmp(cp, NFQUEUE_IFACE, sizeof NFQUEUE_IFACE - 1) == 0) 704335640Shselasky cp += sizeof NFQUEUE_IFACE - 1; 705335640Shselasky else { 706335640Shselasky /* Nope, doesn't begin with NFLOG_IFACE nor NFQUEUE_IFACE */ 707335640Shselasky *is_ours = 0; 708335640Shselasky return NULL; 709335640Shselasky } 710335640Shselasky 711335640Shselasky /* 712335640Shselasky * Yes - is that either the end of the name, or is it followed 713335640Shselasky * by a colon? 714335640Shselasky */ 715335640Shselasky if (*cp != ':' && *cp != '\0') { 716335640Shselasky /* Nope */ 717335640Shselasky *is_ours = 0; 718335640Shselasky return NULL; 719335640Shselasky } 720335640Shselasky 721335640Shselasky /* OK, it's probably ours. */ 722335640Shselasky *is_ours = 1; 723335640Shselasky 724335640Shselasky p = pcap_create_common(ebuf, sizeof (struct pcap_netfilter)); 725335640Shselasky if (p == NULL) 726335640Shselasky return (NULL); 727335640Shselasky 728335640Shselasky p->activate_op = netfilter_activate; 729335640Shselasky return (p); 730335640Shselasky} 731335640Shselasky 732335640Shselaskyint 733335640Shselaskynetfilter_findalldevs(pcap_if_list_t *devlistp, char *err_str) 734335640Shselasky{ 735335640Shselasky int sock; 736335640Shselasky 737335640Shselasky sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); 738335640Shselasky if (sock < 0) { 739335640Shselasky /* if netlink is not supported this is not fatal */ 740335640Shselasky if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) 741335640Shselasky return 0; 742335640Shselasky pcap_fmt_errmsg_for_errno(err_str, PCAP_ERRBUF_SIZE, 743335640Shselasky errno, "Can't open netlink socket"); 744335640Shselasky return -1; 745335640Shselasky } 746335640Shselasky close(sock); 747335640Shselasky 748335640Shselasky /* 749335640Shselasky * The notion of "connected" vs. "disconnected" doesn't apply. 750335640Shselasky * XXX - what about "up" and "running"? 751335640Shselasky */ 752335640Shselasky if (add_dev(devlistp, NFLOG_IFACE, 753335640Shselasky PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, 754335640Shselasky "Linux netfilter log (NFLOG) interface", err_str) == NULL) 755335640Shselasky return -1; 756335640Shselasky if (add_dev(devlistp, NFQUEUE_IFACE, 757335640Shselasky PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, 758335640Shselasky "Linux netfilter queue (NFQUEUE) interface", err_str) == NULL) 759335640Shselasky return -1; 760335640Shselasky return 0; 761335640Shselasky} 762