/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_pkt_queue_netemu.c,v $ $Revision: 1.23.10.1 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_pkt_queue_netemu.c,v $ * $Author: kenneth $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* Network emulator. This queue can - Add latency - Drop packets randomly or at a deterministic rate - Corrupt packets randomly or at a deterministic rate */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPNET_USE_IFO_NETEMU #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include "ipnet.h" #include "ipnet_h.h" #include "ipnet_pkt_queue.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPNET_NETEMU_DROP_PATTERN_INACTIVE 255 #define IPNET_NETEMU_SET_PKT_SEQ_NUM(pkt, seq) \ ipcom_pkt_set_int(pkt, IPNET_PKT_INFO_NETEMU_SEQ_NUM, seq, Ip_u32) #define IPNET_NETEMU_GET_PKT_SEQ_NUM(pkt) \ ipcom_pkt_get_int(pkt, IPNET_PKT_INFO_NETEMU_SEQ_NUM, Ip_u32) /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* netemu queue */ typedef struct Ipnet_pkt_queue_netemu_struct { Ipnet_pkt_queue hdr; Ipcom_pqueue *pq; Ipnet_timeout *tmo; int limit; /* Maximum number of packets that can be queued */ Ip_u32 enqueue_count; /* Total number of packets that has been queued */ Ip_u32 min_latency_msec; /* Minimum latency added to packets in milliseconds */ Ip_u32 max_latency_msec; /* Maximum latency added to packets in milliseconds */ Ip_bool random_drop; /* IP_FALSE -> drop every drop_probability packet IP_TRUE -> there is 1/every drop_probability chance that at packet is dropped */ Ip_u32 drop_probability; /* 1 means every packet dropped, 10 means that every 10 packet is dropped */ Ip_u32 drop_pattern; /* Bit field where 1 means drop and 0 means pass */ Ip_u8 drop_pattern_len; /* Number of bits that is valid in 'drop_pattern' */ Ip_u8 drop_pattern_index; /* Current bit used */ Ip_bool random_corrupt; /* IP_FALSE -> corrupt every corrupt_probability packet IP_TRUE -> there is 1/every corrupt_probability chance that a packet is corrupted */ Ip_u32 corrupt_probability;/* 1 means every packet corrupted, 10 means that every 10 packet is corrupted */ Ip_u32 seq; /* Sequence number that is increased each time a packet is added */ } Ipnet_pkt_queue_netemu; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_pkt_queue_netemu_pq_cmp *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_pkt_queue_netemu_pq_cmp(const Ipcom_pkt *pkt1, const Ipcom_pkt *pkt2) { if (IPNET_PKT_GET_TIMEOUT_ABS(pkt1) == IPNET_PKT_GET_TIMEOUT_ABS(pkt2)) return IPCOM_IS_LT(IPNET_NETEMU_GET_PKT_SEQ_NUM(pkt1), IPNET_NETEMU_GET_PKT_SEQ_NUM(pkt2)) ? 1 : -1; if (IPCOM_IS_LT(IPNET_PKT_GET_TIMEOUT_ABS(pkt1), IPNET_PKT_GET_TIMEOUT_ABS(pkt2))) return 1; return -1; } /* *=========================================================================== * ipnet_pkt_queue_netemu_corrupt *=========================================================================== * Description: Inserts a packet into the queue. * Parameters: pkt - The packet to corrupt * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_corrupt(Ipcom_pkt *pkt) { int i; int c = 1 + ipcom_rand() % 10; for (i = 0; i < c; i++) pkt->data[pkt->start + ipcom_rand() % (pkt->end - pkt->start)] = (Ip_u8) ipcom_rand(); } /* *=========================================================================== * ipnet_pkt_queue_netemu_to_string *=========================================================================== * Description: * Parameters: * Returns: * */ #ifdef IPNET_DEBUG IP_STATIC int ipnet_pkt_queue_netemu_to_string(Ipnet_pkt_queue_netemu *netemu, char *buf, Ip_size_t buf_len) { return ipcom_snprintf(buf, buf_len, "NetEmu if %s limit %d size %d", netemu->hdr.netif->ipcom.name, netemu->limit, ipcom_pqueue_size(netemu->pq)); } #endif /* *=========================================================================== * ipnet_pkt_queue_netemu_retry_cb *=========================================================================== * Description: Retries to transmit packets queued in the 'netemu' queue * Parameters: netemu - a netemu-queue instance * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_retry_cb(Ipnet_pkt_queue_netemu *netemu) { Ipcom_pkt *pkt; Ipnet_pkt_queue *q = netemu->hdr.netif->snd_queue; while (IP_NULL != (pkt = q->dequeue(q))) { int ret = ipnet_if_drv_output(q->netif, pkt); if (ret < 0) { q->requeue(q, pkt); break; } } } /* *=========================================================================== * ipnet_pkt_queue_netemu_enqueue *=========================================================================== * Description: Inserts a packet into the queue. * Parameters: q - A NETEMU based queue. * pkt - The packet to store. * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_pkt_queue_netemu_enqueue(Ipnet_pkt_queue_netemu *q, Ipcom_pkt *pkt) { Ip_u32 tmo_msec; Ip_bool drop = IP_FALSE; Ip_bool corrupt = IP_FALSE; if (ipcom_pqueue_size(q->pq) == q->limit) { IPCOM_LOG0(DEBUG, "NETEMU enqueue: queue full"); return -IP_ERRNO_ENOBUFS; } ++q->enqueue_count; if (q->drop_probability) { Ip_bool restart_pattern = IP_FALSE; if(q->random_drop) restart_pattern = !((Ip_u32) ipcom_rand() % q->drop_probability); else restart_pattern = !(q->enqueue_count % q->drop_probability); if (restart_pattern) { if (q->drop_pattern_len == 0) drop = IP_TRUE; else q->drop_pattern_index = 0; } if (q->drop_pattern_index < q->drop_pattern_len) { drop = IP_BIT_ISSET(q->drop_pattern, 1 << q->drop_pattern_index); ++q->drop_pattern_index; } } if (drop) { IPCOM_LOG0(DEBUG, "NETEMU enqueue: dropping packet"); return -IP_ERRNO_ECOMM; } if (q->corrupt_probability) { if (q->random_corrupt) corrupt = !((Ip_u32) ipcom_rand() % q->corrupt_probability); else corrupt = !(q->enqueue_count % q->corrupt_probability); } if (corrupt) { Ipcom_pkt *corrupt_pkt; /* Must clone the packet since IPNET does not expect the data be corrupted on the original packet */ corrupt_pkt = ipnet_pkt_clone(pkt, IP_FALSE); if (corrupt_pkt != IP_NULL) { IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_NETIF_DEQUEUE); ipcom_pkt_free(pkt); pkt = corrupt_pkt; IPCOM_LOG0(DEBUG, "NETEMU enqueue: corrupting packet"); ipnet_pkt_queue_netemu_corrupt(pkt); } } tmo_msec = q->min_latency_msec; if (q->max_latency_msec > q->min_latency_msec) tmo_msec += (Ip_u32) ipcom_rand() % (q->max_latency_msec - q->min_latency_msec); ipnet->msec_now = ipnet_msec_now(); IPNET_PKT_SET_TIMEOUT_ABS(pkt, tmo_msec + ipnet->msec_now); IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_NETIF_OUTQ); IPNET_NETEMU_SET_PKT_SEQ_NUM(pkt, ++q->seq); return ipcom_pqueue_insert(q->pq, pkt) == IPCOM_SUCCESS ? 0 : -IP_ERRNO_ENOMEM; } /* *=========================================================================== * ipnet_pkt_queue_netemu_dequeue *=========================================================================== * Description: Removed a packet from the queue. * Parameters: q - A NETEMU based queue. * Returns: A packet from the queue or IP_NULL if the queue is empty. * */ IP_STATIC Ipcom_pkt * ipnet_pkt_queue_netemu_dequeue(Ipnet_pkt_queue_netemu *q) { Ipcom_pkt *pkt; Ip_s64 msec_now; pkt = ipcom_pqueue_get_next(q->pq); if (pkt == IP_NULL) return IP_NULL; msec_now = ipnet->msec_now; if (IPCOM_IS_GT64(IPNET_PKT_GET_TIMEOUT_ABS(pkt), msec_now)) { /* More latency to add */ if (q->tmo == IP_NULL) { (void)ipnet_timeout_schedule(IPNET_PKT_GET_TIMEOUT_ABS(pkt) - msec_now, (Ipnet_timeout_handler) ipnet_pkt_queue_netemu_retry_cb, q, &q->tmo); } return IP_NULL; } IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_NETIF_DEQUEUE); return ipcom_pqueue_remove_next(q->pq); } /* *=========================================================================== * ipnet_pkt_queue_netemu_requeue *=========================================================================== * Description: Puts back a packet so it will be the first to dequeue next * time. * Parameters: netemu - A NETEMU packet queue. * * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_requeue(Ipnet_pkt_queue_netemu *netemu, Ipcom_pkt *pkt) { (void)ipcom_pqueue_insert(netemu->pq, pkt); } /* *=========================================================================== * ipnet_pkt_queue_netemu_count *=========================================================================== * Description: Returns the number of packets that is currently queued. * Parameters: netemu - A NETEMU based queue. * Returns: * */ IP_STATIC int ipnet_pkt_queue_netemu_count(Ipnet_pkt_queue_netemu *netemu) { return ipcom_pqueue_size(netemu->pq); } /* *=========================================================================== * ipnet_pkt_queue_netemu_reset *=========================================================================== * Description: Resets the queue, all packets is discarded. * Parameters: netemu - A NETEMU based queue. * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_reset(Ipnet_pkt_queue_netemu *netemu) { Ipcom_pkt *pkt; while (IP_NULL != (pkt = ipcom_pqueue_remove_next(netemu->pq))) { IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_NETIF_DEQUEUE); ipcom_pkt_free(pkt); } } /* *=========================================================================== * ipnet_pkt_queue_netemu_dump *=========================================================================== * Description: Dumps the configuration to the supplied buffer. * Parameters: q - A packet queue. * n - The buffer where the data should be dumped. * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_dump(Ipnet_pkt_queue_netemu *q, struct Ipnet_ifqueue_netemu *n) { n->netemu_limit = q->limit; n->netemu_min_latency = q->min_latency_msec; n->netemu_max_latency = q->max_latency_msec; n->netemu_random_drop = q->random_drop; n->netemu_drop_probability = q->drop_probability; n->netemu_random_corrupt = q->random_corrupt; n->netemu_corrupt_probability = q->corrupt_probability; n->netemu_drop_pattern = q->drop_pattern; n->netemu_drop_pattern_len = q->drop_pattern_len; } /* *=========================================================================== * ipnet_pkt_queue_netemu_configure *=========================================================================== * Description: Configures a queue with new parameters. * Parameters: q - A packet queue. * n - The queue parameters. * Returns: 0 = success * <0 = error code */ IP_STATIC int ipnet_pkt_queue_netemu_configure(Ipnet_pkt_queue_netemu *q, struct Ipnet_ifqueue_netemu *n) { q->limit = n->netemu_limit; q->min_latency_msec = n->netemu_min_latency; q->max_latency_msec = n->netemu_max_latency; q->random_drop = n->netemu_random_drop; q->drop_probability = n->netemu_drop_probability; q->random_corrupt = n->netemu_random_corrupt; q->corrupt_probability = n->netemu_corrupt_probability; q->drop_pattern = n->netemu_drop_pattern; q->drop_pattern_len = n->netemu_drop_pattern_len; return 0; } /* *=========================================================================== * ipnet_pkt_queue_netemu_init *=========================================================================== * Description: Configures a queue with new parameters. * Parameters: q - A packet queue. * Returns: * */ IP_STATIC int ipnet_pkt_queue_netemu_init(Ipnet_pkt_queue_netemu *q) { ipcom_memset((Ip_u8 *) q + sizeof(Ipnet_pkt_queue), 0, sizeof(Ipnet_pkt_queue_netemu) - sizeof(Ipnet_pkt_queue)); q->drop_pattern_index = IPNET_NETEMU_DROP_PATTERN_INACTIVE; q->pq = ipcom_pqueue_new((Ipcom_pqueue_cmp_func) ipnet_pkt_queue_netemu_pq_cmp, ipcom_pqueue_nop_store_index); return 0; } /* *=========================================================================== * ipnet_pkt_queue_netemu_destroy *=========================================================================== * Description: Frees all contained queues. * Parameters: netemu - A NETEMU based queue. * Returns: * */ IP_STATIC void ipnet_pkt_queue_netemu_destroy(Ipnet_pkt_queue_netemu *netemu) { ipnet_timeout_cancel(netemu->tmo); ipnet_pkt_queue_netemu_reset(netemu); ipcom_pqueue_delete(netemu->pq); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_pkt_queue_netemu_template *=========================================================================== * Description: Returns a template for NETEMU queue. * Parameters: * Returns: * */ IP_GLOBAL Ipnet_pkt_queue * ipnet_pkt_queue_netemu_template() { static Ipnet_pkt_queue netemu_template = { "netemu", sizeof(Ipnet_pkt_queue_netemu), IP_NULL, /* Not a container */ (Ipnet_pkt_queue_enqueue) ipnet_pkt_queue_netemu_enqueue, (Ipnet_pkt_queue_dequeue) ipnet_pkt_queue_netemu_dequeue, (Ipnet_pkt_queue_requeue) ipnet_pkt_queue_netemu_requeue, (Ipnet_pkt_queue_count) ipnet_pkt_queue_netemu_count, (Ipnet_pkt_queue_reset) ipnet_pkt_queue_netemu_reset, (Ipnet_pkt_queue_dump) ipnet_pkt_queue_netemu_dump, (Ipnet_pkt_queue_configure) ipnet_pkt_queue_netemu_configure, (Ipnet_pkt_queue_init) ipnet_pkt_queue_netemu_init, (Ipnet_pkt_queue_destroy) ipnet_pkt_queue_netemu_destroy, }; #ifdef IPNET_DEBUG ipnet_timeout_to_string((Ipnet_timeout_handler) ipnet_pkt_queue_netemu_retry_cb, (Ipnet_timeout_to_string_f) ipnet_pkt_queue_netemu_to_string); #endif return &netemu_template; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ #else int ipnet_pkt_queue_netemu_empty_file; #endif /* IPNET_USE_IFO_NETEMU */ /* **************************************************************************** * END OF FILE **************************************************************************** */