| |
| |
| |
| #include "iptraf-ng-compat.h" |
| |
| #include "packet.h" |
| #include "capt.h" |
| |
| struct capt_data_mmap_v2 { |
| void *mmap; |
| size_t mmap_size; |
| struct tpacket2_hdr **hdr; |
| struct sockaddr_ll **sll; |
| unsigned int lastslot; |
| unsigned int slot; |
| }; |
| |
| #define FRAMES 512 |
| #define FRAME_SIZE TPACKET_ALIGN(MAX_PACKET_SIZE + TPACKET2_HDRLEN) |
| |
| static unsigned int capt_mmap_find_filled_slot(struct capt_data_mmap_v2 *data) |
| { |
| for (unsigned int i = data->lastslot; i < data->lastslot + FRAMES; i++) { |
| unsigned int slot = i >= FRAMES ? i - FRAMES : i; |
| |
| if (data->hdr[slot]->tp_status & TP_STATUS_USER) |
| return slot; |
| } |
| return FRAMES; |
| } |
| |
| static bool capt_have_packet_mmap_v2(struct capt *capt) |
| { |
| struct capt_data_mmap_v2 *data = capt->priv; |
| |
| if (capt_mmap_find_filled_slot(data) != FRAMES) |
| return true; |
| else |
| return false; |
| } |
| |
| static int capt_get_packet_mmap_v2(struct capt *capt, struct pkt_hdr *pkt) |
| { |
| struct capt_data_mmap_v2 *data = capt->priv; |
| int ss = 0; |
| |
| unsigned int slot = capt_mmap_find_filled_slot(data); |
| if (slot < FRAMES) { |
| struct tpacket2_hdr *hdr = data->hdr[slot]; |
| struct sockaddr_ll *sll = data->sll[slot]; |
| |
| pkt->pkt_buf = (char *)hdr + hdr->tp_mac; |
| pkt->pkt_payload = NULL; |
| pkt->pkt_caplen = hdr->tp_snaplen; |
| pkt->pkt_len = hdr->tp_len; |
| pkt->from = sll; |
| pkt->pkt_protocol = ntohs(sll->sll_protocol); |
| |
| data->slot = slot; |
| ss = hdr->tp_len; |
| } |
| return ss; |
| } |
| |
| static int capt_put_packet_mmap_v2(struct capt *capt, struct pkt_hdr *pkt __unused) |
| { |
| struct capt_data_mmap_v2 *data = capt->priv; |
| |
| |
| if (data->slot < FRAMES) { |
| data->hdr[data->slot]->tp_status = TP_STATUS_KERNEL; |
| data->lastslot = data->slot; |
| } else |
| data->slot = FRAMES; |
| |
| return 0; |
| } |
| |
| static void capt_cleanup_mmap_v2(struct capt *capt) |
| { |
| struct capt_data_mmap_v2 *data = capt->priv; |
| |
| free(data->sll); |
| data->sll = NULL; |
| |
| free(data->hdr); |
| data->hdr = NULL; |
| |
| munlock(data->mmap, data->mmap_size); |
| munmap(data->mmap, data->mmap_size); |
| data->mmap = NULL; |
| data->mmap_size = 0; |
| |
| free(capt->priv); |
| capt->priv = NULL; |
| |
| capt->cleanup = NULL; |
| capt->put_packet = NULL; |
| capt->get_packet = NULL; |
| capt->have_packet = NULL; |
| } |
| |
| int capt_setup_mmap_v2(struct capt *capt) |
| { |
| if (capt_get_socket(capt) == -1) |
| return -1; |
| |
| int version = TPACKET_V2; |
| if (setsockopt(capt->fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)) != 0) |
| goto err; |
| |
| int hdrlen = version; |
| socklen_t socklen = sizeof(hdrlen); |
| if (getsockopt(capt->fd, SOL_PACKET, PACKET_HDRLEN, &hdrlen, &socklen) != 0) |
| goto err; |
| |
| struct tpacket_req req; |
| |
| req.tp_block_nr = 1; |
| req.tp_frame_nr = FRAMES; |
| req.tp_frame_size = FRAME_SIZE; |
| req.tp_block_size = req.tp_frame_nr * req.tp_frame_size; |
| |
| if (setsockopt(capt->fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) != 0) |
| goto err; |
| |
| size_t size = req.tp_block_size * req.tp_block_nr; |
| void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, capt->fd, 0); |
| if (map == MAP_FAILED) |
| goto err; |
| |
| |
| (void)mlock(map, size); |
| |
| |
| |
| struct capt_data_mmap_v2 *data = xmallocz(sizeof(struct capt_data_mmap_v2)); |
| |
| data->mmap = map; |
| data->mmap_size = size; |
| data->hdr = xmallocz(FRAMES * sizeof(*data->hdr)); |
| data->sll = xmallocz(FRAMES * sizeof(*data->sll)); |
| for (int i = 0; i < FRAMES; i++) { |
| data->hdr[i] = (struct tpacket2_hdr *)((char *)map + i * FRAME_SIZE); |
| data->sll[i] = (struct sockaddr_ll *)((char *)data->hdr[i] + TPACKET_ALIGN(hdrlen)); |
| } |
| data->lastslot = 0; |
| data->slot = FRAMES; |
| |
| capt->priv = data; |
| capt->have_packet = capt_have_packet_mmap_v2; |
| capt->get_packet = capt_get_packet_mmap_v2; |
| capt->put_packet = capt_put_packet_mmap_v2; |
| capt->cleanup = capt_cleanup_mmap_v2; |
| |
| return 0; |
| err: |
| capt_put_socket(capt); |
| return -1; |
| } |