You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.7 KiB
307 lines
8.7 KiB
/*
|
|
* send_packets.c: from tcpreplay tools by Aaron Turner
|
|
* http://tcpreplay.sourceforge.net/
|
|
* send_packets.c is under BSD license (see below)
|
|
* SIPp is under GPL license
|
|
*
|
|
*
|
|
* Copyright (c) 2001-2004 Aaron Turner.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright owners nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*Map linux structure fields to BSD ones*/
|
|
#ifdef __LINUX
|
|
#define __BSD_SOURCE
|
|
#define _BSD_SOURCE
|
|
#define __FAVOR_BSD
|
|
#endif /*__LINUX*/
|
|
|
|
#include <pcap.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <netinet/udp.h>
|
|
#if defined(__DARWIN) || defined(__CYGWIN) || defined(__FreeBSD__)
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifndef __CYGWIN
|
|
#include <netinet/ip6.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
|
|
#include "send_packets.h"
|
|
#include "prepare_pcap.h"
|
|
#include "screen.hpp"
|
|
|
|
extern volatile unsigned long rtp_pckts_pcap;
|
|
extern volatile unsigned long rtp_bytes_pcap;
|
|
extern int media_ip_is_ipv6;
|
|
|
|
inline void
|
|
timerdiv (struct timeval *tvp, float div)
|
|
{
|
|
double interval;
|
|
|
|
if (div == 0 || div == 1)
|
|
return;
|
|
|
|
interval = ((double) tvp->tv_sec * 1000000 + tvp->tv_usec) / (double) div;
|
|
tvp->tv_sec = interval / (int) 1000000;
|
|
tvp->tv_usec = interval - (tvp->tv_sec * 1000000);
|
|
}
|
|
|
|
/*
|
|
* converts a float to a timeval structure
|
|
*/
|
|
inline void
|
|
float2timer (float time, struct timeval *tvp)
|
|
{
|
|
float n;
|
|
|
|
n = time;
|
|
|
|
tvp->tv_sec = n;
|
|
|
|
n -= tvp->tv_sec;
|
|
tvp->tv_usec = n * 100000;
|
|
}
|
|
|
|
/* buffer should be "file_name" */
|
|
int
|
|
parse_play_args (char *buffer, pcap_pkts *pkts)
|
|
{
|
|
pkts->file = strdup (buffer);
|
|
prepare_pkts(pkts->file, pkts);
|
|
return 1;
|
|
}
|
|
|
|
void hexdump(char *p, int s) {
|
|
int i;
|
|
for (i = 0; i < s; i++) {
|
|
fprintf(stderr, "%02x ", *(char *)(p+i));
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
/*Safe threaded version*/
|
|
void do_sleep (struct timeval *, struct timeval *,
|
|
struct timeval *, struct timeval *);
|
|
void send_packets_cleanup(void *arg)
|
|
{
|
|
int sock = (int) arg;
|
|
|
|
// Close send socket
|
|
close(sock);
|
|
}
|
|
|
|
|
|
int
|
|
send_packets (play_args_t * play_args)
|
|
{
|
|
int ret, sock, port_diff;
|
|
pcap_pkt *pkt_index, *pkt_max;
|
|
uint16_t *from_port, *to_port;
|
|
struct timeval didsleep = { 0, 0 };
|
|
struct timeval start = { 0, 0 };
|
|
struct timeval last = { 0, 0 };
|
|
pcap_pkts *pkts = play_args->pcap;
|
|
/* to and from are pointers in case play_args (call sticky) gets modified! */
|
|
struct sockaddr_storage *to = &(play_args->to);
|
|
struct sockaddr_storage *from = &(play_args->from);
|
|
struct udphdr *udp;
|
|
struct sockaddr_in6 to6, from6;
|
|
char buffer[PCAP_MAXPACKET];
|
|
int temp_sum;
|
|
|
|
#ifndef MSG_DONTWAIT
|
|
int fd_flags;
|
|
#endif
|
|
|
|
if (media_ip_is_ipv6) {
|
|
sock = socket(PF_INET6, SOCK_RAW, IPPROTO_UDP);
|
|
if (sock < 0) {
|
|
ERROR("Can't create raw socket (need to run as root?)");
|
|
}
|
|
from_port = &(((struct sockaddr_in6 *)(void *) from )->sin6_port);
|
|
to_port = &(((struct sockaddr_in6 *)(void *) to )->sin6_port);
|
|
}
|
|
else {
|
|
sock = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
|
|
if (sock < 0) {
|
|
ERROR("Can't create raw socket (need to run as root?)");
|
|
}
|
|
from_port = &(((struct sockaddr_in *)(void *) from )->sin_port);
|
|
to_port = &(((struct sockaddr_in *)(void *) to )->sin_port);
|
|
}
|
|
|
|
#ifndef MSG_DONTWAIT
|
|
fd_flags = fcntl(sock, F_GETFL , NULL);
|
|
fd_flags |= O_NONBLOCK;
|
|
fcntl(sock, F_SETFL , fd_flags);
|
|
#endif
|
|
udp = (struct udphdr *)buffer;
|
|
|
|
pkt_index = pkts->pkts;
|
|
pkt_max = pkts->max;
|
|
|
|
if (media_ip_is_ipv6) {
|
|
memset(&to6, 0, sizeof(to6));
|
|
memset(&from6, 0, sizeof(from6));
|
|
to6.sin6_family = AF_INET6;
|
|
from6.sin6_family = AF_INET6;
|
|
memcpy(&(to6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) to)->sin6_addr.s6_addr), sizeof(to6.sin6_addr.s6_addr));
|
|
memcpy(&(from6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) from)->sin6_addr.s6_addr), sizeof(from6.sin6_addr.s6_addr));
|
|
}
|
|
|
|
|
|
/* Ensure the sender socket is closed when the thread exits - this
|
|
* allows the thread to be cancelled cleanly.
|
|
*/
|
|
pthread_cleanup_push(send_packets_cleanup, ((void *) sock));
|
|
|
|
|
|
while (pkt_index < pkt_max) {
|
|
memcpy(udp, pkt_index->data, pkt_index->pktlen);
|
|
port_diff = ntohs (udp->uh_dport) - pkts->base;
|
|
// modify UDP ports
|
|
udp->uh_sport = htons(port_diff + *from_port);
|
|
udp->uh_dport = htons(port_diff + *to_port);
|
|
|
|
if (!media_ip_is_ipv6) {
|
|
temp_sum = checksum_carry(pkt_index->partial_check + check((u_int16_t *) &(((struct sockaddr_in *)(void *) from)->sin_addr.s_addr), 4) + check((u_int16_t *) &(((struct sockaddr_in *)(void *) to)->sin_addr.s_addr), 4) + check((u_int16_t *) &udp->uh_sport, 4));
|
|
}
|
|
else {
|
|
temp_sum = checksum_carry(pkt_index->partial_check + check((u_int16_t *) &(from6.sin6_addr.s6_addr), 16) + check((u_int16_t *) &(to6.sin6_addr.s6_addr), 16) + check((u_int16_t *) &udp->uh_sport, 4));
|
|
}
|
|
|
|
#ifndef _HPUX_LI
|
|
#ifdef __HPUX
|
|
udp->uh_sum = (temp_sum>>16)+((temp_sum & 0xffff)<<16);
|
|
#else
|
|
udp->uh_sum = temp_sum;
|
|
#endif
|
|
#else
|
|
udp->uh_sum = temp_sum;
|
|
#endif
|
|
|
|
do_sleep ((struct timeval *) &pkt_index->ts, &last, &didsleep,
|
|
&start);
|
|
#ifdef MSG_DONTWAIT
|
|
if (!media_ip_is_ipv6) {
|
|
ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT,
|
|
(struct sockaddr *)(void *) to, sizeof(struct sockaddr_in));
|
|
}
|
|
else {
|
|
ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT,
|
|
(struct sockaddr *)(void *) &to6, sizeof(struct sockaddr_in6));
|
|
}
|
|
#else
|
|
if (!media_ip_is_ipv6) {
|
|
ret = sendto(sock, buffer, pkt_index->pktlen, 0,
|
|
(struct sockaddr *)(void *) to, sizeof(struct sockaddr_in));
|
|
}
|
|
else {
|
|
ret = sendto(sock, buffer, pkt_index->pktlen, 0,
|
|
(struct sockaddr *)(void *) &to6, sizeof(struct sockaddr_in6));
|
|
}
|
|
#endif
|
|
if (ret < 0) {
|
|
close(sock);
|
|
WARNING("send_packets.c: sendto failed with error: %s", strerror(errno));
|
|
return( -1);
|
|
}
|
|
|
|
rtp_pckts_pcap++;
|
|
rtp_bytes_pcap += pkt_index->pktlen - sizeof(*udp);
|
|
memcpy (&last, &(pkt_index->ts), sizeof (struct timeval));
|
|
pkt_index++;
|
|
}
|
|
|
|
/* Closing the socket is handled by pthread_cleanup_push()/pthread_cleanup_pop() */
|
|
pthread_cleanup_pop(1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Given the timestamp on the current packet and the last packet sent,
|
|
* calculate the appropriate amount of time to sleep and do so.
|
|
*/
|
|
void do_sleep (struct timeval *time, struct timeval *last,
|
|
struct timeval *didsleep, struct timeval *start)
|
|
{
|
|
struct timeval nap, now, delta;
|
|
struct timespec sleep;
|
|
|
|
if (gettimeofday (&now, NULL) < 0)
|
|
{
|
|
fprintf (stderr, "Error gettimeofday: %s\n", strerror (errno));
|
|
}
|
|
|
|
/* First time through for this file */
|
|
if (!timerisset (last))
|
|
{
|
|
*start = now;
|
|
timerclear (&delta);
|
|
timerclear (didsleep);
|
|
}
|
|
else
|
|
{
|
|
timersub (&now, start, &delta);
|
|
}
|
|
|
|
if (timerisset (last) && timercmp (time, last, >))
|
|
{
|
|
timersub (time, last, &nap);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Don't sleep if this is our first packet, or if the
|
|
* this packet appears to have been sent before the
|
|
* last packet.
|
|
*/
|
|
timerclear (&nap);
|
|
}
|
|
|
|
timeradd (didsleep, &nap, didsleep);
|
|
|
|
if (timercmp (didsleep, &delta, >))
|
|
{
|
|
timersub (didsleep, &delta, &nap);
|
|
|
|
sleep.tv_sec = nap.tv_sec;
|
|
sleep.tv_nsec = nap.tv_usec * 1000; /* convert ms to ns */
|
|
|
|
while ((nanosleep (&sleep, &sleep) == -1) && (errno == -EINTR));
|
|
}
|
|
}
|
|
|
|
|