Commit ade0d273 authored by Christophe Massiot's avatar Christophe Massiot

Add support for changing the source address with raw IPv4 sockets, patch

by Benjamin Cohen.
parent f0b6a67d
......@@ -7,6 +7,10 @@
# The fields are: name (N), email (E), web-address (W), CVS account login (C),
# PGP key ID and fingerprint (P), description (D), and snail-mail address (S).
N: Benjamin Cohen
E: bencoh AT notk DOT org
D: raw sockets
N: Christophe Massiot
E: massiot AT via DOT ecp DOT fr
C: massiot
......
......@@ -72,6 +72,8 @@ Options include:
/ttl=XX (time-to-live of the UDP packet)
/tos=XX (sets the IPv4 Type Of Service option)
/tcp (binds a TCP socket instead of UDP)
/srcaddr=XXX.XXX.XXX.XXX (source address for raw packets)
/srcportr=XX (source port for raw packets)
Example:
239.255.0.1:5004/ttl=64
......
......@@ -279,7 +279,7 @@ int main( int i_argc, char **pp_argv )
break;
case 'X':
i_retx_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_retx_tcp );
i_retx_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_retx_tcp, NULL );
if ( i_retx_fd == -1 )
{
msg_Err( NULL, "unable to set up retx with %s\n", optarg );
......@@ -315,7 +315,7 @@ int main( int i_argc, char **pp_argv )
usage();
i_input_fd = OpenSocket( pp_argv[optind], 0, DEFAULT_PORT, 0, NULL,
&b_input_tcp );
&b_input_tcp, NULL );
if ( i_input_fd == -1 )
{
msg_Err( NULL, "unable to open input socket" );
......@@ -331,7 +331,7 @@ int main( int i_argc, char **pp_argv )
p_outputs = realloc( p_outputs, ++i_nb_outputs * sizeof(output_t) );
p_outputs[i_nb_outputs - 1].i_fd =
OpenSocket( pp_argv[optind++], i_ttl, 0, DEFAULT_PORT,
&p_outputs[i_nb_outputs - 1].i_weight, NULL );
&p_outputs[i_nb_outputs - 1].i_weight, NULL, NULL );
if ( p_outputs[i_nb_outputs - 1].i_fd == -1 )
{
msg_Err( NULL, "unable to open output socket" );
......
......@@ -75,6 +75,8 @@ static bool b_input_udp = false, b_output_udp = false;
static size_t i_asked_payload_size = DEFAULT_PAYLOAD_SIZE;
static size_t i_rtp_header_size = RTP_HEADER_SIZE;
static uint64_t i_rotate_size = DEFAULT_ROTATE_SIZE;
struct udprawpkt pktheader;
bool b_raw_packets = false;
static volatile sig_atomic_t b_die = 0;
static uint16_t i_rtp_seqnum;
......@@ -91,7 +93,7 @@ void (*pf_ExitWrite)(void);
static void usage(void)
{
msg_Raw( NULL, "Usage: multicat [-i <RT priority>] [-t <ttl>] [-X] [-T <file name>] [-f] [-p <PCR PID>] [-s <chunks>] [-n <chunks>] [-k <start time>] [-d <duration>] [-a] [-r <file duration>] [-S <SSRC IP>] [-u] [-U] [-m <payload size>] [-R <RTP header size>] <input item> <output item>" );
msg_Raw( NULL, "Usage: multicat [-i <RT priority>] [-t <ttl>] [-X] [-T <file name>] [-f] [-p <PCR PID>] [-s <chunks>] [-n <chunks>] [-k <start time>] [-d <duration>] [-a] [-r <file duration>] [-S <SSRC IP>] [-u] [-U] [-m <payload size>] [-R <RTP header size>] [-w] <input item> <output item>" );
msg_Raw( NULL, " item format: <file path | device path | FIFO path | directory path | network host>" );
msg_Raw( NULL, " host format: [<connect addr>[:<connect port>]][@[<bind addr][:<bind port>]]" );
msg_Raw( NULL, " -X: also pass-through all packets to stdout" );
......@@ -109,6 +111,7 @@ static void usage(void)
msg_Raw( NULL, " -U: destination has no RTP header" );
msg_Raw( NULL, " -m: size of the payload chunk, excluding optional RTP header (default 1316)" );
msg_Raw( NULL, " -R: size of the optional RTP header (default 12)" );
msg_Raw( NULL, " -w: send with RAW (needed for /srcaddr)" );
exit(EXIT_FAILURE);
}
......@@ -240,7 +243,7 @@ static int udp_InitRead( const char *psz_arg, size_t i_len,
off_t i_nb_skipped_chunks, int64_t i_pos )
{
if ( i_pos || (i_input_fd = OpenSocket( psz_arg, i_ttl, DEFAULT_PORT, 0,
NULL, &b_tcp )) < 0 )
NULL, &b_tcp, NULL )) < 0 )
return -1;
i_udp_nb_skips = i_nb_skipped_chunks;
......@@ -254,6 +257,38 @@ static int udp_InitRead( const char *psz_arg, size_t i_len,
return 0;
}
static ssize_t raw_Write( const void *p_buf, size_t i_len )
{
ssize_t i_ret;
struct iovec iov[2];
#ifdef __FAVOR_BSD
pktheader.udph.uh_ulen
#else
pktheader.udph.len
#endif
= htons(sizeof(struct udphdr) + i_len);
iov[0].iov_base = &pktheader;
iov[0].iov_len = sizeof(struct udprawpkt);
iov[1].iov_base = (void *) p_buf;
iov[1].iov_len = i_len;
if ( (i_ret = writev( i_output_fd, iov, 2 )) < 0 )
{
if ( errno == EBADF || errno == ECONNRESET || errno == EPIPE )
{
msg_Err( NULL, "write error (%s)", strerror(errno) );
b_die = 1;
}
/* otherwise do not set b_die because these errors can be transient */
return 0;
}
return i_ret;
}
/* Please note that the write functions also work for TCP */
static ssize_t udp_Write( const void *p_buf, size_t i_len )
{
......@@ -279,11 +314,20 @@ static void udp_ExitWrite(void)
static int udp_InitWrite( const char *psz_arg, size_t i_len, bool b_append )
{
struct opensocket_opt opt;
memset(&opt, 0, sizeof(struct opensocket_opt));
if (b_raw_packets) {
opt.p_raw_pktheader = &pktheader;
}
if ( (i_output_fd = OpenSocket( psz_arg, i_ttl, 0, DEFAULT_PORT,
NULL, NULL )) < 0 )
NULL, NULL, &opt )) < 0 )
return -1;
pf_Write = udp_Write;
if (b_raw_packets) {
pf_Write = raw_Write;
} else {
pf_Write = udp_Write;
}
pf_ExitWrite = udp_ExitWrite;
return 0;
}
......@@ -727,7 +771,7 @@ int main( int i_argc, char **pp_argv )
sigset_t set;
/* Parse options */
while ( (c = getopt( i_argc, pp_argv, "i:t:XT:fp:s:n:k:d:ar:S:uUm:R:h" )) != -1 )
while ( (c = getopt( i_argc, pp_argv, "i:t:XT:fp:s:n:k:d:ar:S:uUm:R:wh" )) != -1 )
{
switch ( c )
{
......@@ -808,6 +852,10 @@ int main( int i_argc, char **pp_argv )
i_rtp_header_size = strtol( optarg, NULL, 0 );
break;
case 'w':
b_raw_packets = true;
break;
case 'h':
default:
usage();
......
......@@ -441,7 +441,7 @@ int main( int i_argc, char **pp_argv )
break;
case 'X':
i_retx_fd = i_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_tcp );
i_retx_fd = i_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_tcp, NULL );
if ( i_fd == -1 )
{
msg_Err( NULL, "unable to set up retx with %s\n", optarg );
......@@ -475,7 +475,7 @@ int main( int i_argc, char **pp_argv )
while ( optind < i_argc - 1 )
{
i_fd = OpenSocket( pp_argv[optind], 0, DEFAULT_PORT, 0, NULL,
&b_tcp );
&b_tcp, NULL );
if ( i_fd == -1 )
{
msg_Err( NULL, "unable to open input %s\n", pp_argv[optind] );
......@@ -495,7 +495,7 @@ int main( int i_argc, char **pp_argv )
msg_Dbg( NULL, "%d retx passes", i_nb_retx );
i_output_fd = OpenSocket( pp_argv[optind], i_ttl, 0, DEFAULT_PORT, NULL,
NULL );
NULL, NULL );
if ( i_output_fd == -1 )
{
msg_Err( NULL, "unable to open output %s\n", pp_argv[optind] );
......
......@@ -45,7 +45,7 @@
#include "util.h"
#undef DEBUG_SOCKET
#define DEBUG_SOCKET
/*****************************************************************************
* Local declarations
......@@ -390,11 +390,81 @@ static struct addrinfo *ParseNodeService( char *_psz_string, char **ppsz_end,
return p_res;
}
/*****************************************************************************
* RawFillHeaders - fill ip/udp headers for RAW socket
*****************************************************************************/
static void RawFillHeaders(struct udprawpkt *dgram,
in_addr_t ipsrc, in_addr_t ipdst,
uint16_t portsrc, uint16_t portdst,
uint8_t ttl, uint8_t tos, uint16_t len)
{
struct iphdr *iph = &(dgram->iph);
struct udphdr *udph = &(dgram->udph);
#ifdef DEBUG_SOCKET
char ipsrc_str[16], ipdst_str[16];
struct in_addr insrc, indst;
insrc.s_addr = ipsrc;
indst.s_addr = ipdst;
strncpy(ipsrc_str, inet_ntoa(insrc), 16);
strncpy(ipdst_str, inet_ntoa(indst), 16);
printf("Filling raw header (%p) (%s:%u -> %s:%u)\n", dgram, ipsrc_str, portsrc, ipdst_str, portdst);
#endif
// Fill ip header
iph->ihl = 5; // ip header with no specific option
iph->version = 4;
iph->tos = tos;
iph->tot_len = sizeof(struct udprawpkt) + len; // auto-htoned ?
iph->id = htons(0); // auto-generated if frag_off (flags) = 0 ?
iph->frag_off = 0;
iph->ttl = ttl;
iph->protocol = IPPROTO_UDP;
iph->check = 0;
iph->saddr = ipsrc;
iph->daddr = ipdst;
// Fill udp header
#ifdef __FAVOR_BSD
udph->uh_sport = htons(portsrc);
udph->uh_dport = htons(portdst);
udph->uh_ulen = htons(sizeof(struct udphdr) + len);
udph->uh_sum = 0;
#else
udph->source = htons(portsrc);
udph->dest = htons(portdst);
udph->len = htons(sizeof(struct udphdr) + len);
udph->check = 0;
#endif
// Compute ip header checksum. Computed by kernel when frag_off = 0 ?
//iph->check = csum((unsigned short *)iph, sizeof(struct iphdr));
}
/*****************************************************************************
* OpenSocket: parse argv and open IPv4 & IPv6 sockets
*****************************************************************************/
static char *config_stropt( char *psz_string )
{
char *ret, *tmp;
if ( !psz_string || strlen( psz_string ) == 0 )
return NULL;
ret = tmp = strdup( psz_string );
while (*tmp) {
if (*tmp == '_')
*tmp = ' ';
if (*tmp == '/') {
*tmp = '\0';
break;
}
tmp++;
}
return ret;
}
int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
uint16_t i_connect_port, unsigned int *pi_weight, bool *pb_tcp )
uint16_t i_connect_port, unsigned int *pi_weight, bool *pb_tcp,
struct opensocket_opt *p_opt)
{
sockaddr_t bind_addr, connect_addr;
int i_fd, i;
......@@ -408,6 +478,10 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
struct addrinfo *p_ai;
int i_family;
socklen_t i_sockaddr_len;
bool b_host = false;
bool b_raw_packets = false;
in_addr_t i_raw_srcaddr = INADDR_ANY;
int i_raw_srcport = 0;
bind_addr.ss.ss_family = AF_UNSPEC;
connect_addr.ss.ss_family = AF_UNSPEC;
......@@ -436,6 +510,7 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
/* Hosts */
if ( psz_token[0] != '@' )
{
b_host = true;
p_ai = ParseNodeService( psz_token, &psz_token, i_connect_port,
&i_connect_if_index );
if ( p_ai == NULL )
......@@ -462,6 +537,13 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
#ifdef DEBUG_SOCKET
PrintSocket( "socket definition:", &bind_addr, &connect_addr );
#endif
/* opensocket optional struct */
if (p_opt) {
if (p_opt->p_raw_pktheader) {
memset(p_opt->p_raw_pktheader, 0, sizeof(struct udprawpkt));
b_raw_packets = true;
}
}
/* Weights and options */
if ( psz_token2 )
......@@ -477,13 +559,25 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
i_bind_if_index = i_connect_if_index =
strtol( ARG_OPTION("ifindex="), NULL, 0 );
else if ( IS_OPTION("ifaddr=") )
{
char *option = config_stropt( ARG_OPTION("ifaddr=") );
i_if_addr = inet_addr( ARG_OPTION("ifaddr=") );
free( option );
}
else if ( IS_OPTION("ttl=") )
i_ttl = strtol( ARG_OPTION("ttl="), NULL, 0 );
else if ( IS_OPTION("tos=") )
i_tos = strtol( ARG_OPTION("tos="), NULL, 0 );
else if ( IS_OPTION("tcp") )
*pb_tcp = true;
else if ( IS_OPTION("srcaddr=") )
{
char *option = config_stropt( ARG_OPTION("srcaddr=") );
i_raw_srcaddr = inet_addr( option );
free( option );
}
else if ( IS_OPTION("srcport=") )
i_raw_srcport = strtol( ARG_OPTION("srcport="), NULL, 0 );
else
msg_Warn( NULL, "unrecognized option %s", psz_token2 );
......@@ -525,8 +619,15 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
else i_connect_if_index = i_bind_if_index;
/* Socket configuration */
if ( (i_fd = socket( i_family, *pb_tcp ? SOCK_STREAM : SOCK_DGRAM,
0 )) < 0 )
if (b_raw_packets && b_host) {
RawFillHeaders(p_opt->p_raw_pktheader,
i_raw_srcaddr, connect_addr.sin.sin_addr.s_addr, i_raw_srcport,
ntohs(connect_addr.sin.sin_port), i_ttl, i_tos, 0);
i_fd = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
} else {
i_fd = socket( i_family, *pb_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
}
if ( i_fd < 0 )
{
msg_Err( NULL, "unable to open socket (%s)", strerror(errno) );
exit(EXIT_FAILURE);
......
......@@ -21,6 +21,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <netinet/udp.h>
#include <netinet/ip.h>
#define HAVE_CLOCK_NANOSLEEP
#define DEFAULT_PORT 1234
......@@ -33,6 +36,24 @@
#define VERB_INFO 2
#define VERB_WARN 1
/*****************************************************************************
* Raw udp packet structure with flexible-array payload
*****************************************************************************/
struct udprawpkt {
struct iphdr iph;
struct udphdr udph;
uint8_t payload[];
} __attribute__((packed));
/*****************************************************************************
* OpenSocket options
*****************************************************************************/
struct opensocket_opt {
struct udprawpkt *p_raw_pktheader;
};
/*****************************************************************************
* Prototypes
*****************************************************************************/
......@@ -46,7 +67,8 @@ void wall_Sleep( uint64_t i_delay );
uint64_t real_Date( void );
void real_Sleep( uint64_t i_delay );
int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
uint16_t i_connect_port, unsigned int *pi_weight, bool *pb_tcp );
uint16_t i_connect_port, unsigned int *pi_weight, bool *pb_tcp,
struct opensocket_opt *p_opt);
mode_t StatFile(const char *psz_arg);
int OpenFile( const char *psz_arg, bool b_read, bool b_append );
char *GetAuxFile( const char *psz_arg, size_t i_payload_size );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment