diff options
author | Ting-Wei Lan <lantw44@gmail.com> | 2014-06-28 17:51:53 +0800 |
---|---|---|
committer | Ting-Wei Lan <lantw44@gmail.com> | 2014-06-28 17:51:53 +0800 |
commit | 6d8e1841befc8ed958ff45ad2aef2d3907fe6c94 (patch) | |
tree | 128df6b2ad9c51d8abba0074dd71b0e17b447f6b | |
parent | 57db8e639f512e5607eef3becd60510ad8f10251 (diff) | |
download | fastalg-nfqueue-6d8e1841befc8ed958ff45ad2aef2d3907fe6c94.tar.gz fastalg-nfqueue-6d8e1841befc8ed958ff45ad2aef2d3907fe6c94.tar.zst fastalg-nfqueue-6d8e1841befc8ed958ff45ad2aef2d3907fe6c94.zip |
Update TCP packet processing framework and support sending SYN-ACK
-rw-r--r-- | src/falgnfq-loop.c | 270 | ||||
-rw-r--r-- | src/falgnfq-private.h | 25 | ||||
-rw-r--r-- | src/falgnfq-tcp.c | 62 | ||||
-rw-r--r-- | src/falgnfq-tcp.h | 11 |
4 files changed, 314 insertions, 54 deletions
diff --git a/src/falgnfq-loop.c b/src/falgnfq-loop.c index 628a29b..88d4f9e 100644 --- a/src/falgnfq-loop.c +++ b/src/falgnfq-loop.c @@ -1,5 +1,8 @@ /* vim: set sw=4 ts=4 sts=4 et: */ +#define USE_PKT_INFO +#define USE_QUEUED_PKT + #include "config.h" #include "falgnfq-config.h" #include "falgnfq-dump.h" @@ -33,8 +36,11 @@ #include <netinet/ip6.h> #include <netinet/tcp.h> #include <netinet/udp.h> +#include <poll.h> #include <stdint.h> #include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> // XXX: Workaround buggy libnetfilter_queue functions #ifdef LIBNETFILTER_QUEUE_IS_VERY_BUGGY @@ -57,10 +63,15 @@ struct falgnfq_loop { FalgnfqConfig* config; struct proto_info proto; // (cache) protocol info struct mnl_socket* nl; // netlink socket + int nl_fd; // netlink socket file descriptor unsigned portid; // (cache) netlink socket portid size_t pkt_max; // maximal possible netlink packet GHashTable* pkts; // a hash table of FalgprotoPacket - int rng; // random number generator + int rng; // (TCP only) random number generator + int raw_ip; // (TCP only) raw IPv4 / IPv6 socket + GQueue raw_ip_q; // (TCP only) raw IPv4 / IPv6 packet queue + int raw_tcp; // (TCP only) raw TCP socket + GQueue raw_tcp_q; // (TCP only) raw TCP packet queue }; /* We only process one transport layer protocol (TCP or UDP), so @@ -75,16 +86,18 @@ struct falgnfq_loop { #define UDP_STATUS(x) ((struct udp_status*)(x)) typedef struct transport_status { - FalgprotoPacket* last; + FalgprotoPacket* last; } TransportStatus; typedef struct tcp_status { - TransportStatus inherited; - FalgnfqTcp* tcp; + TransportStatus inherited; + FalgnfqTcp* tcp; + struct sockaddr_storage addr; + socklen_t addr_len; } TcpStatus; typedef struct udp_status { - TransportStatus inherited; + TransportStatus inherited; } UdpStatus; /* The first item in the list is not used to store packets. @@ -123,11 +136,18 @@ static inline FalgprotoPacket* packet_list_new (size_t head_data_size) { return list; } -static FalgprotoPacket* packet_list_tcp_new (void) { +static FalgprotoPacket* packet_list_tcp_new ( + struct sockaddr_storage *addr, socklen_t addr_len, + GQueue *queue_ip, GQueue *queue_tcp) { + FalgprotoPacket *list = packet_list_new (sizeof (TcpStatus)); // Constructor code of TcpStatus goes here - TCP_STATUS (list->data)->tcp = falgnfq_tcp_new (); + TcpStatus *status = TCP_STATUS (list->data); + status->tcp = falgnfq_tcp_new ( + SOCKADDR (addr), addr_len, queue_ip, queue_tcp); + status->addr = *addr; + status->addr_len = addr_len; return list; } @@ -151,6 +171,7 @@ static void packet_list_append ( info->id = id; info->mark = mark; info->pktb = pktb; + info->network_header = pktb_network_header (pktb); info->transport_header = transport_header; item->next = NULL; @@ -399,9 +420,8 @@ static bool tcp_inspect ( return true; } - if (!falgnfq_tcp_client ( - TCP_STATUS (list->data)->tcp, loop->rng, - PKT_FIRST (list), PKT_LAST (list))) { + if (!falgnfq_tcp_client (TCP_STATUS (list->data)->tcp, + loop->rng, PKT_FIRST (list), PKT_LAST (list))) { *verdict = loop->config->default_mark; return true; @@ -556,6 +576,8 @@ static int queue_cb (const struct nlmsghdr *nlh, void *loop_generic) { return MNL_CB_ERROR; } + struct sockaddr_storage addr; + socklen_t addr_len; switch (loop->config->family) { case AF_INET: { struct iphdr *iph = nfq_ip_get_hdr (pktb); @@ -568,6 +590,10 @@ static int queue_cb (const struct nlmsghdr *nlh, void *loop_generic) { error (" packet id %" PRIu32 ", truncated IPv4 packet", pkt_id); } + SOCKADDR_IN (&addr)->sin_family = AF_INET; + SOCKADDR_IN (&addr)->sin_addr.s_addr = iph->saddr; + addr_len = sizeof (struct sockaddr_in); + debug (" packet id %" PRIu32 ", %s", pkt_id, iph->protocol == IPPROTO_TCP ? "layer 4 is TCP" : iph->protocol == IPPROTO_UDP ? "layer 4 is UDP" : @@ -594,6 +620,10 @@ static int queue_cb (const struct nlmsghdr *nlh, void *loop_generic) { error (" packet id %" PRIu32 ", truncated IPv6 packet", pkt_id); } + SOCKADDR_IN6 (&addr)->sin6_family = AF_INET6; + SOCKADDR_IN6 (&addr)->sin6_addr = ip6h->ip6_src; + addr_len = sizeof (struct sockaddr_in6); + if_debug (1) { char print_buf[2048]; nfq_ip6_snprintf (print_buf, 2048, ip6h); @@ -630,10 +660,26 @@ static int queue_cb (const struct nlmsghdr *nlh, void *loop_generic) { debug (" packet id %" PRIu32 ", %s", pkt_id, print_buf); } + switch (addr.ss_family) { + case AF_INET: + SOCKADDR_IN (&addr)->sin_port = th->source; + break; + + case AF_INET6: + SOCKADDR_IN6 (&addr)->sin6_port = th->source; + break; + + // This should never happen + default: + error ("UNEXPECTED ERROR: unknown address family"); + abort (); + } + key = PACKET_KEY (ntohs (th->source), ntohs (th->dest)); FalgprotoPacket *list = g_hash_table_lookup (loop->pkts, key); if (list == NULL) { - list = packet_list_tcp_new (); + list = packet_list_tcp_new ( + &addr, addr_len, &loop->raw_ip_q, &loop->raw_tcp_q); g_hash_table_insert (loop->pkts, key, list); } @@ -660,6 +706,21 @@ static int queue_cb (const struct nlmsghdr *nlh, void *loop_generic) { debug (" packet id %" PRIu32 ", %s", pkt_id, print_buf); } + switch (addr.ss_family) { + case AF_INET: + SOCKADDR_IN (&addr)->sin_port = uh->source; + break; + + case AF_INET6: + SOCKADDR_IN6 (&addr)->sin6_port = uh->source; + break; + + // This should never happen + default: + error ("UNEXPECTED ERROR: unknown address family"); + abort (); + } + key = PACKET_KEY (ntohs (uh->source), ntohs (uh->dest)); FalgprotoPacket *list = g_hash_table_lookup (loop->pkts, key); if (list == NULL) { @@ -700,6 +761,7 @@ FalgnfqLoop* falgnfq_loop_new (FalgnfqConfig *config) { if (mnl_socket_bind (nl, 0, MNL_SOCKET_AUTOPID) < 0) { error ("mnl_socket_bind: %s\n", ERRMSG); + mnl_socket_close (nl); return NULL; } @@ -713,7 +775,7 @@ FalgnfqLoop* falgnfq_loop_new (FalgnfqConfig *config) { nfq_nlmsg_cfg_put_cmd(nlh, (uint16_t)(config->family), NFQNL_CFG_CMD_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { error ("mnl_socket_sendto: NFQNL_CFG_CMD_BIND: %s\n", ERRMSG); - return NULL; + goto free_nl; } // set queue number and options @@ -733,20 +795,14 @@ FalgnfqLoop* falgnfq_loop_new (FalgnfqConfig *config) { NFQA_CFG_F_CONNTRACK)); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { error ("mnl_socket_sendto: NFQA_CFG_FLAGS: %s\n", ERRMSG); - return NULL; - } - - int rng = falgnfq_rng_new (); - if (rng < 0) { - error ("Fail to open the random number generator: %s\n", ERRMSG); - return NULL; + goto free_nl; } // allocate the struct and return FalgnfqLoop *loop = malloc (sizeof (FalgnfqLoop)); if (loop == NULL) { error ("malloc: %s\n", ERRMSG); - return NULL; + goto free_nl; } loop->config = config; @@ -755,19 +811,53 @@ FalgnfqLoop* falgnfq_loop_new (FalgnfqConfig *config) { loop->proto.printer = falgproto_get_printer (config->protocol); loop->proto.matcher = falgproto_get_matcher (config->protocol); loop->nl = nl; + loop->nl_fd = mnl_socket_get_fd (nl); loop->portid = mnl_socket_get_portid (nl); loop->pkt_max = pkt_max; - loop->rng = rng; switch (loop->proto.transport) { - case FALGPROTO_TRANSPORT_TCP: + case FALGPROTO_TRANSPORT_TCP: { + loop->rng = falgnfq_rng_new (); + if (loop->rng < 0) { + error ("Fail to open the random number generator: %s", ERRMSG); + goto free_loop; + } + + int one = 1; + loop->raw_ip = socket (loop->config->family, SOCK_RAW, IPPROTO_RAW); + if (loop->raw_ip < 0) { + error ("Fail to open a raw socket: %s", ERRMSG); + goto free_rng; + } + if (setsockopt (loop->raw_ip, IPPROTO_IP, IP_HDRINCL, + &one, sizeof(one)) < 0) { + error ("setsockopt IP_HDRINCL = 1: %s", ERRMSG); + goto free_raw_ip; + } + + int zero = 0; + loop->raw_tcp = socket (loop->config->family, SOCK_RAW, IPPROTO_TCP); + if (loop->raw_tcp < 0) { + error ("Fail to open a raw socket: %s", ERRMSG); + goto free_raw_ip; + } + if (setsockopt (loop->raw_tcp, IPPROTO_IP, IP_HDRINCL, + &zero, sizeof (zero)) < 0) { + error ("setsockopt IP_HDRINCL = 0: %s", ERRMSG); + goto free_raw_tcp; + } + loop->pkts = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, packet_list_tcp_free); - break; - case FALGPROTO_TRANSPORT_UDP: + g_queue_init (&loop->raw_ip_q); + g_queue_init (&loop->raw_tcp_q); + } break; + + case FALGPROTO_TRANSPORT_UDP: { loop->pkts = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, packet_list_udp_free); - break; + } break; + default: error ( "UNEXPECTED ERROR: unknown transport layer protocol " @@ -778,6 +868,19 @@ FalgnfqLoop* falgnfq_loop_new (FalgnfqConfig *config) { debug ("FalgnfqLoop new -> %p", loop); return loop; + +free_raw_tcp: + close (loop->raw_tcp); +free_raw_ip: + close (loop->raw_ip); +free_rng: + falgnfq_rng_free (loop->rng); +free_loop: + free (loop); +free_nl: + mnl_socket_close (nl); +free_nothing: + return NULL; } int falgnfq_loop_run (FalgnfqLoop *loop) { @@ -785,34 +888,108 @@ int falgnfq_loop_run (FalgnfqLoop *loop) { char pkt[loop->pkt_max]; debug ("FalgnfqLoop %p run", loop); + + enum { + POLL_NETLINK, + POLL_RAW_IP, + POLL_RAW_TCP, + POLL_MAX + }; + struct pollfd fds[POLL_MAX] = { + [POLL_NETLINK] = { .fd = loop->nl_fd, .events = POLLIN | POLLPRI }, + [POLL_RAW_IP] = { .fd = loop->raw_ip, .events = POLLOUT }, + [POLL_RAW_TCP] = { .fd = loop->raw_tcp, .events = POLLOUT } + }; + #ifndef NDEBUG while (!falgnfq_exit) { #else while (true) { #endif - debug ("FalgnfqLoop %p run: mnl_socket_recvfrom", loop); - ssize_t pkt_rval = mnl_socket_recvfrom (loop->nl, pkt, loop->pkt_max); - if (pkt_rval < 0) { - if (errno == ENOBUFS) { - warning ("mnl_socket_recvfrom: %s", ERRMSG); - continue; - } else if (errno == EINTR || errno == EWOULDBLOCK) { - debug ("FalnfqLoop %p: mnl_socket_recvfrom interrupted", loop); + // Prevent endless loops + if (g_queue_is_empty (&loop->raw_ip_q)) { + debug ("FalgnfqLoop %p run: disable raw IP socket", loop); + if (fds[POLL_RAW_IP].fd > 0) { + fds[POLL_RAW_IP].fd = - fds[POLL_RAW_IP].fd; + } + } else { + debug ("FalgnfqLoop %p run: enable raw IP socket", loop); + if (fds[POLL_RAW_IP].fd < 0) { + fds[POLL_RAW_IP].fd = - fds[POLL_RAW_IP].fd; + } + } + + if (g_queue_is_empty (&loop->raw_tcp_q)) { + debug ("FalgnfqLoop %p run: disable raw TCP socket", loop); + if (fds[POLL_RAW_TCP].fd > 0) { + fds[POLL_RAW_TCP].fd = - fds[POLL_RAW_TCP].fd; + } + } else { + debug ("FalgnfqLoop %p run: enable raw TCP socket", loop); + if (fds[POLL_RAW_TCP].fd < 0) { + fds[POLL_RAW_TCP].fd = - fds[POLL_RAW_TCP].fd; + } + } + + debug ("FalgnfqLoop %p run: poll", loop); + if (poll (fds, POLL_MAX, -1) < 0) { + if (errno == EINTR || errno == EWOULDBLOCK) { + debug ("FalnfqLoop %p run: poll interrupted", loop); continue; } else { - error ("mnl_socket_recvfrom: %s", ERRMSG); + error ("poll: %s", ERRMSG); return -1; } } - size_t pkt_len = (size_t)pkt_rval; - debug ("FalgnfqLoop %p run: mnl_cb_run", loop); - if (mnl_cb_run (pkt, pkt_len, 0, loop->portid, queue_cb, loop) < 0) { - error ("mnl_cb_run: %s", ERRMSG); - if_debug (1) { - error ("DEVELOPER_MODE: UNEXPECTED ERROR, EXIT NOW!"); - return -1; + if (fds[POLL_NETLINK].revents & POLLIN || + fds[POLL_NETLINK].revents & POLLPRI) { + + debug ("FalgnfqLoop %p run: netlink socket is ready", loop); + debug ("FalgnfqLoop %p run: mnl_socket_recvfrom", loop); + ssize_t pkt_rval = + mnl_socket_recvfrom (loop->nl, pkt, loop->pkt_max); + if (pkt_rval < 0) { + if (errno == ENOBUFS) { + warning ("mnl_socket_recvfrom: %s", ERRMSG); + continue; + } else { + error ("mnl_socket_recvfrom: %s", ERRMSG); + return -1; + } + } + + size_t pkt_len = (size_t)pkt_rval; + debug ("FalgnfqLoop %p run: mnl_cb_run", loop); + if (mnl_cb_run (pkt, pkt_len, 0, loop->portid, queue_cb, loop) < 0) { + error ("mnl_cb_run: %s", ERRMSG); + if_debug (1) { + error ("DEVELOPER_MODE: UNEXPECTED ERROR, EXIT NOW!"); + return -1; + } + } + } + + if (fds[POLL_RAW_IP].revents & POLLOUT) { + debug ("FalgnfqLoop %p run: raw IP socket is ready", loop); + } + + if (fds[POLL_RAW_TCP].revents & POLLOUT) { + debug ("FalgnfqLoop %p run: raw TCP socket is ready", loop); + + QueuedPkt *qpkt = g_queue_pop_tail (&loop->raw_tcp_q); + if (sendto (loop->raw_tcp, qpkt->data, qpkt->len, 0, + SOCKADDR (&qpkt->addr), qpkt->addr_len) < 0) { + + error ("sendto: %s", ERRMSG); + if_debug (1) { + error ("DEVELOPER_MODE: UNEXPECTED ERROR, EXIT NOW!"); + free (qpkt); + return -1; + } } + + free (qpkt); } } @@ -823,5 +1000,16 @@ void falgnfq_loop_free (FalgnfqLoop *loop) { debug ("FalgnfqLoop %p free", loop); mnl_socket_close (loop->nl); g_hash_table_destroy (loop->pkts); + if (loop->proto.transport == FALGPROTO_TRANSPORT_TCP) { + falgnfq_rng_free (loop->rng); + close (loop->raw_ip); + close (loop->raw_tcp); + while (!g_queue_is_empty (&loop->raw_ip_q)) { + free (g_queue_pop_tail (&loop->raw_ip_q)); + } + while (!g_queue_is_empty (&loop->raw_tcp_q)) { + free (g_queue_pop_tail (&loop->raw_tcp_q)); + } + } free (loop); } diff --git a/src/falgnfq-private.h b/src/falgnfq-private.h index 6515903..a6255a5 100644 --- a/src/falgnfq-private.h +++ b/src/falgnfq-private.h @@ -2,7 +2,6 @@ #ifndef FALGNFQ_PRIVATE_H #define FALGNFQ_PRIVATE_H -#include <stdint.h> #include <signal.h> #include <stdio.h> #include <string.h> @@ -69,6 +68,9 @@ static inline char* errmsg (int errnum, char *errbuf, size_t errlen) { // Packet info +#ifdef USE_PKT_INFO + +#include <stdint.h> #define PKT_INFO(x) ((struct pkt_info*)(x)) struct pkt_buff; @@ -76,7 +78,28 @@ typedef struct pkt_info { uint32_t id; uint32_t mark; struct pkt_buff* pktb; + void* network_header; void* transport_header; } PktInfo; +#endif // USE_PKT_INFO + + +// TCP or IP packet in the sending queue + +#ifdef USE_QUEUED_PKT + +#include <stddef.h> +#include <sys/socket.h> +#include <sys/types.h> + +typedef struct queued_pkt { + struct sockaddr_storage addr; + socklen_t addr_len; + size_t len; + char data[]; +} QueuedPkt; + +#endif // USE_QUEUED_PKT + #endif /* FALGNFQ_PRIVATE_H */ diff --git a/src/falgnfq-tcp.c b/src/falgnfq-tcp.c index c1739ac..dc5f2db 100644 --- a/src/falgnfq-tcp.c +++ b/src/falgnfq-tcp.c @@ -1,11 +1,15 @@ /* vim: set sw=4 ts=4 sts=4 et: */ +#define USE_PKT_INFO +#define USE_QUEUED_PKT + #include "config.h" #include "falgnfq-private.h" #include "falgnfq-rng.h" #include "falgnfq-tcp.h" #include <arpa/inet.h> +#include <errno.h> #include <glib.h> #include <inttypes.h> #include <libnetfilter_queue/pktbuff.h> @@ -13,6 +17,8 @@ #include <netinet/tcp.h> #include <stddef.h> #include <stdint.h> +#include <stdlib.h> +#include <string.h> enum { STATE_INITIAL, @@ -26,19 +32,35 @@ enum { struct falgnfq_tcp { unsigned int state; + + struct sockaddr* addr; + socklen_t addr_len; + GQueue* queue_ip; + GQueue* queue_tcp; + struct pkt_buff* client_syn; struct tcphdr* client_syn_th; struct pkt_buff* client_ack; struct tcphdr* client_ack_th; + uint32_t client_seq; uint32_t our_seq; }; -FalgnfqTcp* falgnfq_tcp_new (void) { +FalgnfqTcp* falgnfq_tcp_new ( + struct sockaddr *addr, socklen_t addr_len, + GQueue *queue_ip, GQueue *queue_tcp) { + FalgnfqTcp *tcp = g_slice_alloc (sizeof (FalgnfqTcp)); tcp->state = STATE_INITIAL; + tcp->addr = addr; + tcp->addr_len = addr_len; + tcp->queue_ip = queue_ip; + tcp->queue_tcp = queue_tcp; tcp->client_syn = NULL; + tcp->client_syn_th = NULL; tcp->client_ack = NULL; + tcp->client_ack_th = NULL; return tcp; } @@ -50,24 +72,28 @@ static void tcp_fill_checksum (void *pkt, size_t len) { uint32_t checksum = 0; for (; len > 1; len -= sizeof (uint16_t)) { - checksum += *word; + checksum += (*word); word++; - if (checksum > ((uint32_t)1 << 16)) { - checksum &= (uint32_t)0xffff; - checksum++; - } + } + if (len) { + checksum += *(uint8_t*)(word); } - th->check = (uint16_t)(~checksum); + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + th->check = ((uint16_t)(~checksum)); + debug (" %s: checksum is 0x%04" PRIx16 " (%" PRIu16 ")", + __func__, ntohs (th->check), ntohs (th->check)); } bool falgnfq_tcp_client (FalgnfqTcp *tcp, int rng, FalgprotoPacket *pkt_first, FalgprotoPacket *pkt_last) { + ERRMSG_INIT; + switch (tcp->state) { case STATE_INITIAL: { struct pkt_info *info = pkt_last->data; - struct pkt_buff *pktb = info->pktb; struct tcphdr *th = info->transport_header; if (!th->syn) { @@ -88,7 +114,18 @@ bool falgnfq_tcp_client (FalgnfqTcp *tcp, int rng, __func__, tcp->our_seq); debug (" %s: [SYN] preparing to send SYN-ACK", __func__); - struct tcphdr synack = { + + const size_t synack_len = sizeof (struct tcphdr); + QueuedPkt *tcppkt = malloc (sizeof (QueuedPkt) + synack_len); + if (tcppkt == NULL) { + error (" %s: malloc: %s", __func__, ERRMSG); + return false; + } + + struct tcphdr *synack = (void*)(tcppkt->data); + + tcppkt->len = synack_len; + *synack = (struct tcphdr) { .source = th->dest, .dest = th->source, .seq = htonl (tcp->our_seq), @@ -99,10 +136,13 @@ bool falgnfq_tcp_client (FalgnfqTcp *tcp, int rng, .window = 0xffff // XXX: use the correct window size }; - const size_t synack_len = sizeof (struct tcphdr); - tcp_fill_checksum (&synack, synack_len); + tcp_fill_checksum (tcppkt->data, tcppkt->len); + memcpy (&tcppkt->addr, tcp->addr, tcp->addr_len); + tcppkt->addr_len = tcp->addr_len; + g_queue_push_tail (tcp->queue_tcp, tcppkt); + tcp->state++; } break; } diff --git a/src/falgnfq-tcp.h b/src/falgnfq-tcp.h index 8823af2..d9a14e3 100644 --- a/src/falgnfq-tcp.h +++ b/src/falgnfq-tcp.h @@ -2,14 +2,23 @@ #ifndef FALGNFQ_TCP_H #define FALGNFQ_TCP_H +#include <glib.h> #include <falgproto.h> #include <stdbool.h> #include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> struct falgnfq_tcp; typedef struct falgnfq_tcp FalgnfqTcp; -FalgnfqTcp* falgnfq_tcp_new (void); +struct falgnfq_config; +typedef struct falgnfq_config FalgnfqConfig; + +FalgnfqTcp* falgnfq_tcp_new (struct sockaddr *addr, + socklen_t addr_len, + GQueue *queue_ip, + GQueue *queue_tcp); bool falgnfq_tcp_client (FalgnfqTcp *tcp, int rng, FalgprotoPacket *pkt_first, FalgprotoPacket *pkt_last); |