static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple) { if (cda[CTA_IP_V4_SRC-1]) tuple->src.v4 = *(u_int32_t *)NFA_DATA(cda[CTA_IP_V4_SRC-1]); if (cda[CTA_IP_V4_DST-1]) tuple->dst.v4 = *(u_int32_t *)NFA_DATA(cda[CTA_IP_V4_DST-1]); }
static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple) { if (cda[CTA_PROTO_SRC_PORT-1]) tuple->l4src.sctp.port = *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SRC_PORT-1]); if (cda[CTA_PROTO_DST_PORT-1]) tuple->l4dst.sctp.port = *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_DST_PORT-1]); }
void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data) { struct nfattr *nfa; int size = NFA_LENGTH(attrlen); nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size)); nfa->nfa_type = attrtype; nfa->nfa_len = size; memcpy(NFA_DATA(nfa), data, attrlen); memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size); }
static int ipv4_nfattr_to_tuple(struct nfattr *tb[], struct nf_conntrack_tuple *t) { if (!tb[CTA_IP_V4_SRC-1] || !tb[CTA_IP_V4_DST-1]) return -EINVAL; if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) return -EINVAL; t->src.u3.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); t->dst.u3.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]); return 0; }
static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple) { if (cda[CTA_PROTO_ICMP_TYPE-1]) tuple->l4dst.icmp.type = *(u_int8_t *)NFA_DATA(cda[CTA_PROTO_ICMP_TYPE-1]); if (cda[CTA_PROTO_ICMP_CODE-1]) tuple->l4dst.icmp.code = *(u_int8_t *)NFA_DATA(cda[CTA_PROTO_ICMP_CODE-1]); if (cda[CTA_PROTO_ICMP_ID-1]) tuple->l4src.icmp.id = *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_ICMP_ID-1]); }
/** * nfexp_parse_expect - translate a netlink message to a conntrack object * \param type do the translation iif the message type is of a certain type * \param nlh pointer to the netlink message * \param exp pointer to the conntrack object * * This is a low level function, use it in case that you require to work * with netlink details via libnfnetlink. Otherwise, we suggest you to * use the high level API. * * The message types are: * * NFEXP_T_NEW: parse messages with new conntracks * NFEXP_T_UPDATE: parse messages with conntrack updates * NFEXP_T_DESTROY: parse messages with conntrack destroy * NFEXP_T_ALL: all message types * * The message type is a flag, therefore the can be combined, ie. * NFEXP_T_NEW | NFEXP_T_DESTROY to parse only new and destroy messages * * On error, NFEXP_T_ERROR is returned and errno is set appropiately. If * the message received is not of the requested type then 0 is returned, * otherwise this function returns the message type parsed. */ int nfexp_parse_expect(enum nf_conntrack_msg_type type, const struct nlmsghdr *nlh, struct nf_expect *exp) { unsigned int flags; int len = nlh->nlmsg_len; struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); struct nfattr *cda[CTA_EXPECT_MAX]; assert(nlh != NULL); assert(exp != NULL); len -= NLMSG_LENGTH(sizeof(struct nfgenmsg)); if (len < 0) { errno = EINVAL; return NFCT_T_ERROR; } flags = __parse_expect_message_type(nlh); if (!(flags & type)) return 0; nfnl_parse_attr(cda, CTA_EXPECT_MAX, NFA_DATA(nfhdr), len); __parse_expect(nlh, cda, exp); return flags; }
static int ipv6_nfattr_to_tuple(struct nfattr *tb[], struct nf_conntrack_tuple *t) { if (!tb[CTA_IP_V6_SRC-1] || !tb[CTA_IP_V6_DST-1]) return -EINVAL; if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) return -EINVAL; memcpy(&t->src.u3.ip6, NFA_DATA(tb[CTA_IP_V6_SRC-1]), sizeof(u_int32_t) * 4); memcpy(&t->dst.u3.ip6, NFA_DATA(tb[CTA_IP_V6_DST-1]), sizeof(u_int32_t) * 4); return 0; }
static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct) { struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1]; struct nfattr *tb[CTA_PROTOINFO_TCP_MAX]; /* updates could not contain anything about the private * protocol info, in that case skip the parsing */ if (!attr) return 0; nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr); if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp)) return -EINVAL; if (!tb[CTA_PROTOINFO_TCP_STATE-1]) return -EINVAL; write_lock_bh(&tcp_lock); ct->proto.tcp.state = *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]); write_unlock_bh(&tcp_lock); return 0; }
static int icmp_nfattr_to_tuple(struct nfattr *tb[], struct ip_conntrack_tuple *tuple) { if (!tb[CTA_PROTO_ICMP_TYPE-1] || !tb[CTA_PROTO_ICMP_CODE-1] || !tb[CTA_PROTO_ICMP_ID-1]) return -EINVAL; tuple->dst.u.icmp.type = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]); tuple->dst.u.icmp.code = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]); tuple->src.u.icmp.id = *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]); if (tuple->dst.u.icmp.type >= sizeof(invmap) || !invmap[tuple->dst.u.icmp.type]) return -EINVAL; return 0; }
static int icmp_nfattr_to_tuple(struct nfattr *tb[], struct nf_conntrack_tuple *tuple) { if (!tb[CTA_PROTO_ICMP_TYPE-1] || !tb[CTA_PROTO_ICMP_CODE-1] || !tb[CTA_PROTO_ICMP_ID-1]) return -EINVAL; if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) return -EINVAL; tuple->dst.u.icmp.type = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]); tuple->dst.u.icmp.code = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]); tuple->src.u.icmp.id = *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]); if (tuple->dst.u.icmp.type >= sizeof(invmap) || !invmap[tuple->dst.u.icmp.type]) return -EINVAL; return 0; }
static int nfct_parse_secctx(const struct nlattr *attr, struct nf_conntrack *ct) { struct nlattr *tb[CTA_SECCTX_MAX+1] = {}; if (mnl_attr_parse_nested(attr, nfct_parse_secctx_attr_cb, tb) < 0) return -1; if (!tb[CTA_SECCTX_NAME]) return 0; ct->secctx = strdup(NFA_DATA(tb[CTA_SECCTX_NAME])); if (ct->secctx) set_bit(ATTR_SECCTX, ct->head.set); return 0; }
/** * nfnl_addattr_l - Add variable length attribute to nlmsghdr * @n: netlink message header to which attribute is to be added * @maxlen: maximum length of netlink message header * @type: type of new attribute * @data: content of new attribute * @len: attribute length */ int nfnl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = NFA_LENGTH(alen); struct nfattr *nfa; assert(n); assert(maxlen > 0); assert(type >= 0); if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { errno = ENOSPC; return -1; } nfa = NLMSG_TAIL(n); nfa->nfa_type = type; nfa->nfa_len = len; memcpy(NFA_DATA(nfa), data, alen); n->nlmsg_len = (NLMSG_ALIGN(n->nlmsg_len) + NFA_ALIGN(len)); return 0; }
/** * nfnl_nfa_addattr_l - Add variable length attribute to struct nfattr * * @nfa: struct nfattr * @maxlen: maximal length of nfattr buffer * @type: type for new attribute * @data: content of new attribute * @alen: length of new attribute * */ int nfnl_nfa_addattr_l(struct nfattr *nfa, int maxlen, int type, const void *data, int alen) { struct nfattr *subnfa; int len = NFA_LENGTH(alen); assert(nfa); assert(maxlen > 0); assert(type >= 0); if (NFA_ALIGN(nfa->nfa_len) + len > maxlen) { errno = ENOSPC; return -1; } subnfa = (struct nfattr *)(((char *)nfa) + NFA_ALIGN(nfa->nfa_len)); subnfa->nfa_type = type; subnfa->nfa_len = len; memcpy(NFA_DATA(subnfa), data, alen); nfa->nfa_len = NFA_ALIGN(nfa->nfa_len) + len; return 0; }
static int netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_netfilter *handlep = handle->priv; const unsigned char *buf; int count = 0; int len; /* ignore interrupt system call error */ do { len = recv(handle->fd, handle->buffer, handle->bufsize, 0); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((len == -1) && (errno == EINTR)); if (len < 0) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", errno, pcap_strerror(errno)); return -1; } buf = handle->buffer; while (len >= NLMSG_SPACE(0)) { const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf; u_int32_t msg_len; nftype_t type = OTHER; if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len); return -1; } if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) type = NFLOG; if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET) type = NFQUEUE; if (type != OTHER) { const unsigned char *payload = NULL; struct pcap_pkthdr pkth; const struct nfgenmsg *nfg; int id = 0; if (handle->linktype != DLT_NFLOG) { const struct nfattr *payload_attr = NULL; if (nlh->nlmsg_len < HDR_LENGTH) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); return -1; } nfg = NLMSG_DATA(nlh); if (nlh->nlmsg_len > HDR_LENGTH) { struct nfattr *attr = NFM_NFA(nfg); int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); while (NFA_OK(attr, attr_len)) { if (type == NFQUEUE) { switch (NFA_TYPE(attr)) { case NFQA_PACKET_HDR: { const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr); id = ntohl(pkt_hdr->packet_id); break; } case NFQA_PAYLOAD: payload_attr = attr; break; } } else if (type == NFLOG) { switch (NFA_TYPE(attr)) { case NFULA_PAYLOAD: payload_attr = attr; break; } } attr = NFA_NEXT(attr, attr_len); } } if (payload_attr) { payload = NFA_DATA(payload_attr); pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); } } else { payload = NLMSG_DATA(nlh); pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); } if (payload) { /* pkth.caplen = min (payload_len, handle->snapshot); */ gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, payload); count++; } } if (type == NFQUEUE) { /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */ nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT); } } msg_len = NLMSG_ALIGN(nlh->nlmsg_len); if (msg_len > len) msg_len = len; len -= msg_len; buf += msg_len; } return count; }
/** * nfnl_listen: listen for one or more netlink messages * @nfnhl: libnfnetlink handle * @handler: callback function to be called for every netlink message * - the callback handler should normally return 0 * - but may return a negative error code which will cause * nfnl_listen to return immediately with the same error code * - or return a postivie error code which will cause * nfnl_listen to return after it has finished processing all * the netlink messages in the current packet * Thus a positive error code will terminate nfnl_listen "soon" * without any loss of data, a negative error code will terminate * nfnl_listen "very soon" and throw away data already read from * the netlink socket. * @jarg: opaque argument passed on to callback * * This function is used to receive and process messages coming from an open * nfnetlink handler like events or information request via nfnl_send(). * * On error, -1 is returned, unfortunately errno is not always set * appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_receive_process() instead. */ int nfnl_listen(struct nfnl_handle *nfnlh, int (*handler)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { struct sockaddr_nl nladdr; char buf[NFNL_BUFFSIZE] __attribute__ ((aligned)); struct iovec iov; int remain; struct nlmsghdr *h; struct nlmsgerr *msgerr; int quit=0; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; iov.iov_base = buf; iov.iov_len = sizeof(buf); while (! quit) { remain = recvmsg(nfnlh->fd, &msg, 0); if (remain < 0) { if (errno == EINTR) continue; /* Bad file descriptor */ else if (errno == EBADF) break; else if (errno == EAGAIN) break; nfnl_error("recvmsg overrun: %s", strerror(errno)); continue; } if (remain == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len (%d)", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; remain >= sizeof(*h);) { int err; int len = h->nlmsg_len; int l = len - sizeof(*h); if (l < 0 || len > remain) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); return -1; } nfnl_error("Malformed msg (len=%d)", len); return -1; } /* end of messages reached, let's return */ if (h->nlmsg_type == NLMSG_DONE) return 0; /* Break the loop if success is explicitely * reported via NLM_F_ACK flag set */ if (h->nlmsg_type == NLMSG_ERROR) { msgerr = NLMSG_DATA(h); return msgerr->error; } err = handler(&nladdr, h, jarg); if (err < 0) return err; quit |= err; /* FIXME: why not _NEXT macros, etc.? */ //h = NLMSG_NEXT(h, remain); remain -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("MSG_TRUNC"); continue; } if (remain) { nfnl_error("remnant size %d", remain); return -1; } } return quit; } /** * nfnl_talk - send a request and then receive and process messages returned * @nfnlh: nfnetelink handler * @n: netlink message that contains the request * @peer: peer PID * @groups: netlink groups * @junk: callback called if out-of-sequence messages were received * @jarg: data for the junk callback * * This function is used to request an action that does not returns any * information. On error, a negative value is returned, errno could be * set appropiately. For that reason, the use of this function is DEPRECATED. * Please, use nfnl_query() instead. */ int nfnl_talk(struct nfnl_handle *nfnlh, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer, int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *jarg) { char buf[NFNL_BUFFSIZE] __attribute__ ((aligned)); struct sockaddr_nl nladdr; struct nlmsghdr *h; unsigned int seq; int status; struct iovec iov = { n, n->nlmsg_len }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = peer; nladdr.nl_groups = groups; n->nlmsg_seq = seq = ++nfnlh->seq; /* FIXME: why ? */ if (!answer) n->nlmsg_flags |= NLM_F_ACK; status = sendmsg(nfnlh->fd, &msg, 0); if (status < 0) { nfnl_error("sendmsg(netlink) %s", strerror(errno)); return -1; } iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { status = recvmsg(nfnlh->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; nfnl_error("recvmsg over-run"); continue; } if (status == 0) { nfnl_error("EOF on netlink"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { nfnl_error("Bad sender address len %d", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); int err; if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Truncated message\n"); return -1; } nfnl_error("Malformed message: len=%d\n", len); return -1; /* FIXME: libnetlink exits here */ } if (h->nlmsg_pid != nfnlh->local.nl_pid || h->nlmsg_seq != seq) { if (junk) { err = junk(&nladdr, h, jarg); if (err < 0) return err; } goto cont; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(h); if (l < sizeof(struct nlmsgerr)) nfnl_error("ERROR truncated\n"); else { errno = -err->error; if (errno == 0) { if (answer) memcpy(answer, h, h->nlmsg_len); return 0; } perror("NFNETLINK answers"); } return err->error; } if (answer) { memcpy(answer, h, h->nlmsg_len); return 0; } nfnl_error("Unexpected reply!\n"); cont: status -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { nfnl_error("Messages truncated\n"); continue; } if (status) nfnl_error("Remnant of size %d\n", status); } } /** * nfnl_addattr_l - Add variable length attribute to nlmsghdr * @n: netlink message header to which attribute is to be added * @maxlen: maximum length of netlink message header * @type: type of new attribute * @data: content of new attribute * @len: attribute length */ int nfnl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = NFA_LENGTH(alen); struct nfattr *nfa; assert(n); assert(maxlen > 0); assert(type >= 0); if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { errno = ENOSPC; return -1; } nfa = NLMSG_TAIL(n); nfa->nfa_type = type; nfa->nfa_len = len; memcpy(NFA_DATA(nfa), data, alen); memset((uint8_t *)nfa + nfa->nfa_len, 0, NFA_ALIGN(alen) - alen); n->nlmsg_len = (NLMSG_ALIGN(n->nlmsg_len) + NFA_ALIGN(len)); return 0; }
ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h, const unsigned char *buf, size_t len) { struct nlmsghdr *nlh; struct nfattr *tb[NFULA_MAX]; struct nfulnl_msg_packet_hdr *hdr; if (!h->last_nlh) { printf("first\n"); nlh = nfnl_get_msg_first(nflog_nfnlh(h->nfulh), buf, len); }else { next_msg: printf("next\n"); nlh = nfnl_get_msg_next(nflog_nfnlh(h->nfulh), buf, len); } h->last_nlh = nlh; if (!nlh) return NULL; nfnl_parse_attr(tb, NFULA_MAX, NFM_NFA(NLMSG_DATA(nlh)), NFM_PAYLOAD(nlh)); if (!tb[NFULA_PACKET_HDR-1]) goto next_msg; /* now build the fake ulog_packet_msg */ hdr = NFA_DATA(tb[NFULA_PACKET_HDR-1]); h->upmsg.hook = hdr->hook; if (tb[NFULA_MARK-1]) h->upmsg.mark = ntohl(*(u_int32_t *)NFA_DATA(tb[NFULA_MARK-1])); else h->upmsg.mark = 0; if (tb[NFULA_TIMESTAMP]) { /* FIXME: 64bit network-to-host */ h->upmsg.timestamp_sec = h->upmsg.timestamp_usec = 0; } else h->upmsg.timestamp_sec = h->upmsg.timestamp_usec = 0; if (tb[NFULA_IFINDEX_INDEV-1]) { /* FIXME: ifindex lookup */ h->upmsg.indev_name[0] = '\0'; } else h->upmsg.indev_name[0] = '\0'; if (tb[NFULA_IFINDEX_OUTDEV-1]) { /* FIXME: ifindex lookup */ h->upmsg.outdev_name[0] = '\0'; } else h->upmsg.outdev_name[0] = '\0'; if (tb[NFULA_HWADDR-1]) { struct nfulnl_msg_packet_hw *phw = NFA_DATA(tb[NFULA_HWADDR-1]); h->upmsg.mac_len = ntohs(phw->hw_addrlen); memcpy(h->upmsg.mac, phw->hw_addr, 8); } else h->upmsg.mac_len = 0; if (tb[NFULA_PREFIX-1]) { int plen = NFA_PAYLOAD(tb[NFULA_PREFIX-1]); if (ULOG_PREFIX_LEN < plen) plen = ULOG_PREFIX_LEN; memcpy(h->upmsg.prefix, NFA_DATA(tb[NFULA_PREFIX-1]), plen); h->upmsg.prefix[ULOG_PREFIX_LEN-1] = '\0'; } if (tb[NFULA_PAYLOAD-1]) { memcpy(h->upmsg.payload, NFA_DATA(tb[NFULA_PAYLOAD-1]), NFA_PAYLOAD(tb[NFULA_PAYLOAD-1])); h->upmsg.data_len = NFA_PAYLOAD(tb[NFULA_PAYLOAD-1]); } else h->upmsg.data_len = 0; return &h->upmsg; }
static int nflog_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { const unsigned char *buf; int count = 0; int len; /* ignore interrupt system call error */ do { len = recv(handle->fd, handle->buffer, handle->bufsize, 0); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((len == -1) && (errno == EINTR)); if (len < 0) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", errno, pcap_strerror(errno)); return -1; } buf = handle->buffer; while (len >= NLMSG_SPACE(0)) { const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf; u_int32_t msg_len; if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len); return -1; } if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) { const unsigned char *payload = NULL; struct pcap_pkthdr pkth; if (handle->linktype != DLT_NFLOG) { const struct nfattr *payload_attr = NULL; if (nlh->nlmsg_len < HDR_LENGTH) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); return -1; } if (nlh->nlmsg_len > HDR_LENGTH) { struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); while (NFA_OK(attr, attr_len)) { switch (NFA_TYPE(attr)) { case NFULA_PAYLOAD: payload_attr = attr; break; } attr = NFA_NEXT(attr, attr_len); } } if (payload_attr) { payload = NFA_DATA(payload_attr); pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); } } else { payload = NLMSG_DATA(nlh); pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); } if (payload) { /* pkth.caplen = min (payload_len, handle->snapshot); */ gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) { handle->md.packets_read++; callback(user, &pkth, payload); count++; } } } msg_len = NLMSG_ALIGN(nlh->nlmsg_len); if (msg_len > len) msg_len = len; len -= msg_len; buf += msg_len; } return count; }
static int netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_netfilter *handlep = handle->priv; register u_char *bp, *ep; int count = 0; ssize_t len; /* * Has "pcap_breakloop()" been called? */ if (handle->break_loop) { /* * Yes - clear the flag that indicates that it * has, and return PCAP_ERROR_BREAK to indicate * that we were told to break out of the loop. */ handle->break_loop = 0; return PCAP_ERROR_BREAK; } len = handle->cc; if (len == 0) { /* * The buffer is empty; refill it. * * We ignore EINTR, as that might just be due to a signal * being delivered - if the signal should interrupt the * loop, the signal handler should call pcap_breakloop() * to set handle->break_loop (we ignore it on other * platforms as well). */ do { len = recv(handle->fd, handle->buffer, handle->bufsize, 0); if (handle->break_loop) { handle->break_loop = 0; return PCAP_ERROR_BREAK; } if (errno == ENOBUFS) handlep->packets_nobufs++; } while ((len == -1) && (errno == EINTR || errno == ENOBUFS)); if (len < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); return PCAP_ERROR; } bp = (unsigned char *)handle->buffer; } else bp = handle->bp; ep = bp + len; while (bp < ep) { const struct nlmsghdr *nlh = (const struct nlmsghdr *) bp; uint32_t msg_len; nftype_t type = OTHER; /* * Has "pcap_breakloop()" been called? * If so, return immediately - if we haven't read any * packets, clear the flag and return PCAP_ERROR_BREAK * to indicate that we were told to break out of the loop, * otherwise leave the flag set, so that the *next* call * will break out of the loop without having read any * packets, and return the number of packets we've * processed so far. */ if (handle->break_loop) { handle->bp = bp; handle->cc = (int)(ep - bp); if (count == 0) { handle->break_loop = 0; return PCAP_ERROR_BREAK; } else return count; } if (ep - bp < NLMSG_SPACE(0)) { /* * There's less than one netlink message left * in the buffer. Give up. */ break; } if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || (u_int)len < nlh->nlmsg_len) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %zd) (nlmsg_len: %u)", len, nlh->nlmsg_len); return -1; } if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) type = NFLOG; else if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET) type = NFQUEUE; if (type != OTHER) { const unsigned char *payload = NULL; struct pcap_pkthdr pkth; const struct nfgenmsg *nfg = NULL; int id = 0; if (handle->linktype != DLT_NFLOG) { const struct nfattr *payload_attr = NULL; if (nlh->nlmsg_len < HDR_LENGTH) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); return -1; } nfg = NLMSG_DATA(nlh); if (nlh->nlmsg_len > HDR_LENGTH) { struct nfattr *attr = NFM_NFA(nfg); int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); while (NFA_OK(attr, attr_len)) { if (type == NFQUEUE) { switch (NFA_TYPE(attr)) { case NFQA_PACKET_HDR: { const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr); id = ntohl(pkt_hdr->packet_id); break; } case NFQA_PAYLOAD: payload_attr = attr; break; } } else if (type == NFLOG) { switch (NFA_TYPE(attr)) { case NFULA_PAYLOAD: payload_attr = attr; break; } } attr = NFA_NEXT(attr, attr_len); } } if (payload_attr) { payload = NFA_DATA(payload_attr); pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); } } else { payload = NLMSG_DATA(nlh); pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); } if (payload) { /* pkth.caplen = min (payload_len, handle->snapshot); */ gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || pcap_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, payload); count++; } } if (type == NFQUEUE) { /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */ /* if type == NFQUEUE, handle->linktype is always != DLT_NFLOG, so nfg is always initialized to NLMSG_DATA(nlh). */ if (nfg != NULL) nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT); } } msg_len = NLMSG_ALIGN(nlh->nlmsg_len); /* * If the message length would run past the end of the * buffer, truncate it to the remaining space in the * buffer. */ if (msg_len > ep - bp) msg_len = (uint32_t)(ep - bp); bp += msg_len; if (count >= max_packets && !PACKET_COUNT_IS_UNLIMITED(max_packets)) { handle->bp = bp; handle->cc = (int)(ep - bp); if (handle->cc < 0) handle->cc = 0; return count; } } handle->cc = 0; return count; }
/* This is an inline function, we don't really care about a long * list of arguments */ static inline int __build_packet_message(struct nfulnl_instance *inst, const struct sk_buff *skb, unsigned int data_len, unsigned int pf, unsigned int hooknum, const struct net_device *indev, const struct net_device *outdev, const struct nf_loginfo *li, const char *prefix) { unsigned char *old_tail; struct nfulnl_msg_packet_hdr pmsg; struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; u_int32_t tmp_uint; UDEBUG("entered\n"); old_tail = inst->skb->tail; nlh = NLMSG_PUT(inst->skb, 0, 0, NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET, sizeof(struct nfgenmsg)); nfmsg = NLMSG_DATA(nlh); nfmsg->nfgen_family = pf; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = htons(inst->group_num); pmsg.hw_protocol = htons(skb->protocol); pmsg.hook = hooknum; NFA_PUT(inst->skb, NFULA_PACKET_HDR, sizeof(pmsg), &pmsg); if (prefix) { int slen = strlen(prefix); if (slen > NFULNL_PREFIXLEN) slen = NFULNL_PREFIXLEN; NFA_PUT(inst->skb, NFULA_PREFIX, slen, prefix); } if (indev) { tmp_uint = htonl(indev->ifindex); #ifndef CONFIG_BRIDGE_NETFILTER NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint); #else if (pf == PF_BRIDGE) { /* Case 1: outdev is physical input device, we need to * look for bridge group (when called from * netfilter_bridge) */ NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSINDEV, sizeof(tmp_uint), &tmp_uint); /* this is the bridge group "brX" */ tmp_uint = htonl(indev->br_port->br->dev->ifindex); NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint); } else { /* Case 2: indev is bridge group, we need to look for * physical device (when called from ipv4) */ NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint); if (skb->nf_bridge && skb->nf_bridge->physindev) { tmp_uint = htonl(skb->nf_bridge->physindev->ifindex); NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSINDEV, sizeof(tmp_uint), &tmp_uint); } } #endif } if (outdev) { tmp_uint = htonl(outdev->ifindex); #ifndef CONFIG_BRIDGE_NETFILTER NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint); #else if (pf == PF_BRIDGE) { /* Case 1: outdev is physical output device, we need to * look for bridge group (when called from * netfilter_bridge) */ NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSOUTDEV, sizeof(tmp_uint), &tmp_uint); /* this is the bridge group "brX" */ tmp_uint = htonl(outdev->br_port->br->dev->ifindex); NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint); } else { /* Case 2: indev is a bridge group, we need to look * for physical device (when called from ipv4) */ NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint); if (skb->nf_bridge) { tmp_uint = htonl(skb->nf_bridge->physoutdev->ifindex); NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSOUTDEV, sizeof(tmp_uint), &tmp_uint); } } #endif } if (skb->nfmark) { tmp_uint = htonl(skb->nfmark); NFA_PUT(inst->skb, NFULA_MARK, sizeof(tmp_uint), &tmp_uint); } if (indev && skb->dev && skb->dev->hard_header_parse) { struct nfulnl_msg_packet_hw phw; phw.hw_addrlen = skb->dev->hard_header_parse((struct sk_buff *)skb, phw.hw_addr); phw.hw_addrlen = htons(phw.hw_addrlen); NFA_PUT(inst->skb, NFULA_HWADDR, sizeof(phw), &phw); } if (skb->tstamp.off_sec) { struct nfulnl_msg_packet_timestamp ts; ts.sec = cpu_to_be64(skb->tstamp.off_sec); ts.usec = cpu_to_be64(skb->tstamp.off_usec); NFA_PUT(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts); } /* UID */ if (skb->sk) { read_lock_bh(&skb->sk->sk_callback_lock); if (skb->sk->sk_socket && skb->sk->sk_socket->file) { u_int32_t uid = htonl(skb->sk->sk_socket->file->f_uid); /* need to unlock here since NFA_PUT may goto */ read_unlock_bh(&skb->sk->sk_callback_lock); NFA_PUT(inst->skb, NFULA_UID, sizeof(uid), &uid); } else read_unlock_bh(&skb->sk->sk_callback_lock); } /* local sequence number */ if (inst->flags & NFULNL_CFG_F_SEQ) { tmp_uint = htonl(inst->seq++); NFA_PUT(inst->skb, NFULA_SEQ, sizeof(tmp_uint), &tmp_uint); } /* global sequence number */ if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) { tmp_uint = atomic_inc_return(&global_seq); NFA_PUT(inst->skb, NFULA_SEQ_GLOBAL, sizeof(tmp_uint), &tmp_uint); } if (data_len) { struct nfattr *nfa; int size = NFA_LENGTH(data_len); if (skb_tailroom(inst->skb) < (int)NFA_SPACE(data_len)) { printk(KERN_WARNING "nfnetlink_log: no tailroom!\n"); goto nlmsg_failure; } nfa = (struct nfattr *)skb_put(inst->skb, NFA_ALIGN(size)); nfa->nfa_type = NFULA_PAYLOAD; nfa->nfa_len = size; if (skb_copy_bits(skb, 0, NFA_DATA(nfa), data_len)) BUG(); } nlh->nlmsg_len = inst->skb->tail - old_tail; return 0; nlmsg_failure: UDEBUG("nlmsg_failure\n"); nfattr_failure: PRINTR(KERN_ERR "nfnetlink_log: error creating log nlmsg\n"); return -1; }