int nfnl_check_attributes(const struct nfnl_handle *h, const struct nlmsghdr *nlh, struct nfattr *nfa[]) { assert(h); assert(nlh); assert(nfa); int min_len; uint8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); uint8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type); const struct nfnl_subsys_handle *ssh; struct nfnl_callback *cb; if (subsys_id > NFNL_MAX_SUBSYS) return -EINVAL; ssh = &h->subsys[subsys_id]; cb = &ssh->cb[type]; #if 1 /* checks need to be enabled as soon as this is called from * somebody else than __nfnl_handle_msg */ if (type >= ssh->cb_count) return -EINVAL; min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); if (nlh->nlmsg_len < min_len) return -EINVAL; #endif memset(nfa, 0, sizeof(struct nfattr *) * cb->attr_count); if (nlh->nlmsg_len > min_len) { struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); while (NFA_OK(attr, attrlen)) { unsigned int flavor = NFA_TYPE(attr); if (flavor) { if (flavor > cb->attr_count) { /* we have received an attribute from * the kernel which we don't understand * yet. We have to silently ignore this * for the sake of future compatibility */ attr = NFA_NEXT(attr, attrlen); continue; } nfa[flavor - 1] = attr; } attr = NFA_NEXT(attr, attrlen); } } return 0; }
/** * nfnetlink_check_attributes - check and parse nfnetlink attributes * * subsys: nfnl subsystem for which this message is to be parsed * nlmsghdr: netlink message to be checked/parsed * cda: array of pointers, needs to be at least subsys->attr_count big * */ static int nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys, struct nlmsghdr *nlh, struct nfattr *cda[]) { int min_len; u_int16_t attr_count; u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); if (unlikely(cb_id >= subsys->cb_count)) { DEBUGP("msgtype %u >= %u, returning\n", cb_id, subsys->cb_count); return -EINVAL; } min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); if (unlikely(nlh->nlmsg_len < min_len)) return -EINVAL; attr_count = subsys->cb[cb_id].attr_count; memset(cda, 0, sizeof(struct nfattr *) * attr_count); /* check attribute lengths. */ if (likely(nlh->nlmsg_len > min_len)) { struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); while (NFA_OK(attr, attrlen)) { unsigned flavor = NFA_TYPE(attr); if (flavor) { if (flavor > attr_count) return -EINVAL; cda[flavor - 1] = attr; } attr = NFA_NEXT(attr, attrlen); } } /* implicit: if nlmsg_len == min_len, we return 0, and an empty * (zeroed) cda[] array. The message is valid, but empty. */ return 0; }
/** * nfnetlink_check_attributes - check and parse nfnetlink attributes * * subsys: nfnl subsystem for which this message is to be parsed * nlmsghdr: netlink message to be checked/parsed * cda: array of pointers, needs to be at least subsys->attr_count big * */ static int nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys, struct nlmsghdr *nlh, struct nfattr *cda[]) { int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); u_int16_t attr_count = subsys->cb[cb_id].attr_count; /* check attribute lengths. */ if (likely(nlh->nlmsg_len > min_len)) { struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); nfattr_parse(cda, attr_count, attr, attrlen); } /* implicit: if nlmsg_len == min_len, we return 0, and an empty * (zeroed) cda[] array. The message is valid, but empty. */ return 0; }
void nfnl_dump_packet(struct nlmsghdr *nlh, int received_len, char *desc) { void *nlmsg_data = NLMSG_DATA(nlh); struct nfattr *nfa = NFM_NFA(NLMSG_DATA(nlh)); int len = NFM_PAYLOAD(nlh); printf("%s called from %s\n", __FUNCTION__, desc); printf(" nlmsghdr = %p, received_len = %u\n", nlh, received_len); printf(" NLMSG_DATA(nlh) = %p (+%td bytes)\n", nlmsg_data, (nlmsg_data - (void *)nlh)); printf(" NFM_NFA(NLMSG_DATA(nlh)) = %p (+%td bytes)\n", nfa, ((void *)nfa - (void *)nlh)); printf(" NFM_PAYLOAD(nlh) = %u\n", len); printf(" nlmsg_type = %u, nlmsg_len = %u, nlmsg_seq = %u " "nlmsg_flags = 0x%x\n", nlh->nlmsg_type, nlh->nlmsg_len, nlh->nlmsg_seq, nlh->nlmsg_flags); while (NFA_OK(nfa, len)) { printf(" nfa@%p: nfa_type=%u, nfa_len=%u\n", nfa, NFA_TYPE(nfa), nfa->nfa_len); nfa = NFA_NEXT(nfa,len); } }
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; }
/* On error, -1 is returned and errno is set appropiately. On success, * 0 is returned if there is no more data to process, >0 if there is * more data to process */ static int nfnl_step(struct nfnl_handle *h, struct nlmsghdr *nlh) { struct nfnl_subsys_handle *ssh; uint8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); uint8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type); /* Is this an error message? */ if (nfnl_is_error(h, nlh)) { /* This is an ACK */ if (errno == 0) return 0; /* This an error message */ return -1; } /* nfnetlink sanity checks: check for nfgenmsg size */ if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) { errno = ENOSPC; return -1; } if (subsys_id > NFNL_MAX_SUBSYS) { errno = ENOENT; return -1; } ssh = &h->subsys[subsys_id]; if (!ssh) { errno = ENOENT; return -1; } if (type >= ssh->cb_count) { errno = ENOENT; return -1; } if (ssh->cb[type].attr_count) { int err; struct nfattr *tb[ssh->cb[type].attr_count]; struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); int len = nlh->nlmsg_len - NLMSG_ALIGN(min_len); err = nfnl_parse_attr(tb, ssh->cb[type].attr_count, attr, len); if (err == -1) return -1; if (ssh->cb[type].call) { /* * On error, the callback returns NFNL_CB_FAILURE and * errno must be explicitely set. On success, * NFNL_CB_STOP is returned and we're done, otherwise * NFNL_CB_CONTINUE means that we want to continue * data processing. */ return ssh->cb[type].call(nlh, tb, ssh->cb[type].data); } } /* no callback set, continue data processing */ return 1; }
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; }