Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
rude
rude
Commits
e5ae5948
Commit
e5ae5948
authored
Jul 06, 2015
by
Olav Kvittem
Browse files
-k, -f -C -A
parent
78a4aaa3
Changes
1
Hide whitespace changes
Inline
Side-by-side
crude/main.c
View file @
e5ae5948
...
...
@@ -19,6 +19,10 @@
*
* Authors: Juha Laine <james@cs.tut.fi>
* Sampo Saaristo <sambo@cc.tut.fi>
* 2015-07-02 Olav Kvittem <oak@uninett.no>
* added reading pcap to disk, using kernel timestamp, forking write process to disk
* bug: reading AF_PACKET does not receive any packets
* caveats: only implemented for rec_to_file()
*
*****************************************************************************/
#include <config.h>
...
...
@@ -28,7 +32,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <
linux/
fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
...
...
@@ -40,9 +44,17 @@
#include <sys/mman.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <limits.h>
//#include <pcap.h>
#include <pcap/pcap.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
/* the L2 protocols */
//#include <linux/if_packet.h> // provided by netpacket/packet.h
#define OBUFFERS 10000 // number of records in ouput buffer
#define OCHUNKS 100 // number of records for each write chunk
/*
* Private struct for each flow of runtime statistics
*/
...
...
@@ -82,16 +94,35 @@ static int rec_to_file(unsigned short,unsigned long);
static
int
rec_n_print
(
unsigned
short
,
unsigned
long
);
void
crude_handler
(
int
);
static
void
get_str_addr
(
struct
sockaddr_storage
,
char
[]);
static
int
output_data
(
int
,
int
);
static
int
fork_write
();
/*
* Global variables
*/
int
main_socket
=
0
;
/* The socket to listen to */
int
main_file
=
0
;
/* File to read from/to write to */
int
main_output
=
0
;
/* file to write output (is main_file if not fork) */
unsigned
long
pkt_count
=
0
;
/* Counter for received/processed packets */
struct
flow_stat
*
flows
=
NULL
;
/* List of flows for runtime statistics */
int
nflows
=
0
;
char
preffix
[]
=
"::ffff:"
;
short
opt_af_packet
=
0
,
opt_pcap
=
0
;
short
opt_ktime
=
0
;
// kernel packet time by ioctl
short
opt_fork
=
0
;
// fork for write in separate process with noblock for improving packet loss
char
pcap_dev
[
100
]
=
""
;
pcap_t
*
main_pcap
;
// data structure for ouput ring buffer
int
osize
;
int
obufsize
;
int
ochunksize
;
int
ofree
=
0
;
// next free pos
int
odata
=
0
;
// next data pos
char
*
obuffer
=
NULL
;
int
pipefd
[
2
];
// a pipe for writing to file in a subprocess
int
main
(
int
argc
,
char
**
argv
)
{
...
...
@@ -120,13 +151,16 @@ int main(int argc, char **argv)
"under GNU GENERAL PUBLIC LICENSE Version 2.
\n
"
,
VERSION
);
struct
sockaddr_storage
test
;
printf
(
"Size of sockaddr_storage: %
d
ss_len:%
d
\n
"
,
sizeof
(
test
),
sizeof
(
test
.
ss_family
));
printf
(
"Size of sockaddr_storage: %
lu
ss_len:%
lu
\n
"
,
sizeof
(
test
),
sizeof
(
test
.
ss_family
));
while
((
retval
>=
0
)
&&
((
cmd_char
=
getopt
(
argc
,
argv
,
"hvd:p:i:l:P:n:s:6D:"
))
!=
EOF
))
((
cmd_char
=
getopt
(
argc
,
argv
,
"
A
hvd:p:
C:f
i:
k
l:P:n:s:6D:"
))
!=
EOF
))
{
switch
(
cmd_char
)
{
case
'A'
:
opt_af_packet
=
1
;
break
;
case
'v'
:
if
((
optind
==
2
)
&&
(
argc
==
2
))
{
...
...
@@ -173,6 +207,9 @@ int main(int argc, char **argv)
}
break
;
case
'f'
:
// fork output write
opt_fork
=
1
;
break
;
case
'p'
:
if
(
optarg
!=
NULL
)
{
...
...
@@ -201,6 +238,25 @@ int main(int argc, char **argv)
}
break
;
case
'k'
:
// use kernel time
opt_ktime
=
1
;
// retval=1;
break
;
case
'C'
:
// pcap capture
if
(
optarg
!=
NULL
)
{
errno
=
0
;
opt_pcap
=
1
;
strncpy
(
pcap_dev
,
optarg
,
10
);
}
else
{
RUDEBUG1
(
"crude: invalid commandline arguments!
\n
"
);
retval
=
-
2
;
}
break
;
case
'i'
:
if
(
optarg
!=
NULL
)
{
...
...
@@ -469,6 +525,10 @@ static void usage(char *name)
"
\t
-P priority = process realtime priority {1-90}
\n\n
"
"
\t
-s #[,# ...] = don't record: get runtime stats for specified flows
\n
"
"
\t
-n # = exit automatically after # packets has been logged.
\n
"
"
\t
-k = use kernel time stamps(ioctl).
\n
"
"
\t
-A = use Linux AF_PACKET capture
\n
"
"
\t
-f = fork a write process to avoid write blocks
\n
"
"
\t
-C interface = use pcap packet capture
\n
"
"
\t
use CTRL+C to exit the program otherwise
\n\n
"
,
name
);
}
...
...
@@ -496,7 +556,12 @@ void crude_handler(int value)
}
if
(
nflows
>
0
){
print_stats
();
}
if
(
main_file
>
0
){
close
(
main_file
);
}
if
(
main_file
>
0
){
output_data
(
main_output
,
1
);
close
(
main_file
);
if
(
main_file
!=
main_output
)
// forking
close
(
main_output
);
// will hopefully close the pipe so that the child dies
}
if
(
main_socket
>
0
){
close
(
main_socket
);
}
RUDEBUG1
(
"
\n
crude: captured/processed %lu packets
\n
"
,
pkt_count
);
exit
(
0
);
...
...
@@ -549,22 +614,62 @@ static int make_conn(unsigned short port, char *ifaddr,int use_ip6,unsigned int
return
-
1
;
}
if
((
our_sock
=
socket
(
our_addr
.
ss_family
,
SOCK_DGRAM
,
0
))
<
0
)
{
if
(
opt_af_packet
)
{
// ip packet interface
if
(
(
our_sock
=
socket
(
AF_PACKET
,
SOCK_DGRAM
,
htons
(
ETH_P_IP
|
ETH_P_IPV6
)
)
)
<
0
)
{
RUDEBUG1
(
"crude: socket(AF_PACKET,) failed : %s
\n
"
,
strerror
(
errno
));
}
struct
sockaddr_ll
link
;
memset
(
&
link
,
0
,
sizeof
(
struct
sockaddr_ll
));
link
.
sll_family
=
AF_PACKET
;
link
.
sll_ifindex
=
if_nametoindex
(
"eth0"
);
// link.sll_protocol = ETH_P_IP;
if
(
bind
(
our_sock
,
(
struct
sockaddr
*
)
&
link
,
sizeof
(
struct
sockaddr_ll
))
<
0
)
{
RUDEBUG1
(
"crude: bind(AF_packet) failed: %s
\n
"
,
strerror
(
errno
));
close
(
our_sock
);
return
-
3
;
}
}
else
if
(
opt_pcap
)
{
struct
bpf_program
fp
;
/* The compiled filter */
char
filter
[
100
];
char
errbuf
[
PCAP_ERRBUF_SIZE
];
sprintf
(
filter
,
"udp and port %d"
,
port
);
if
(
(
main_pcap
=
pcap_open_live
(
"eth0"
,
BUFSIZ
,
1
,
1000
,
errbuf
)
)
==
NULL
){
RUDEBUG1
(
"crude: pcap open_live failed : %s
\n
"
,
strerror
(
errno
));
return
(
-
1
);
}
if
(
pcap_compile
(
main_pcap
,
&
fp
,
filter
,
0
,
PCAP_NETMASK_UNKNOWN
)
==
-
1
)
{
RUDEBUG1
(
"Couldn't parse filter %s: %s
\n
"
,
filter
,
pcap_geterr
(
main_pcap
));
return
(
-
1
);
}
if
(
pcap_setfilter
(
main_pcap
,
&
fp
)
==
-
1
)
{
RUDEBUG1
(
"Couldn't install filter %s: %s
\n
"
,
filter
,
pcap_geterr
(
main_pcap
));
return
(
-
1
);
}
}
else
{
if
(
(
our_sock
=
socket
(
our_addr
.
ss_family
,
SOCK_DGRAM
,
0
)
)
<
0
)
{
RUDEBUG1
(
"crude: socket() failed: %s
\n
"
,
strerror
(
errno
));
return
-
2
;
}
if
(
bind
(
our_sock
,
(
struct
sockaddr
*
)
&
our_addr
,
sizeof
(
struct
sockaddr_storage
))
<
0
)
{
RUDEBUG1
(
"crude: bind() failed: %s
\n
"
,
strerror
(
errno
));
close
(
our_sock
);
return
-
3
;
}
if
(
bind
(
our_sock
,
(
struct
sockaddr
*
)
&
our_addr
,
sizeof
(
struct
sockaddr_storage
))
<
0
)
{
RUDEBUG1
(
"crude: bind() failed: %s
\n
"
,
strerror
(
errno
));
close
(
our_sock
);
return
-
3
;
}
}
if
(
isMulticastAddr
(
&
our_addr
)
>
0
)
{
RUDEBUG7
(
"crude: make_conn(): address is a multicast one
\n
"
,
port
,
ifaddr
);
RUDEBUG7
(
"crude: make_conn(): address is a multicast one
: port %d if %s
\n
"
,
port
,
ifaddr
);
if
(
joinGroup
(
our_sock
,
0
,
200
,
&
our_addr
,
ifindex
)
<
0
){
RUDEBUG1
(
"crude: make_conn() failed to join group
\n
"
);
close
(
our_sock
);
...
...
@@ -856,6 +961,13 @@ void print_stats(void)
printf
(
"
\n
"
);
}
static
int
check_response
(
struct
timeval
tw1
,
char
*
msg
){
struct
timeval
tw2
;
gettimeofday
(
&
tw2
,
NULL
);
int
tdiff_us
=
(
tw2
.
tv_sec
-
tw1
.
tv_sec
)
*
10
^
6
+
(
tw2
.
tv_usec
-
tw1
.
tv_usec
);
if
(
tdiff_us
>
500
)
fprintf
(
stderr
,
"used time us: %d - task %s
\n
"
,
tdiff_us
,
msg
);
}
/*
* rec_to_file() - record packets to file in binary format...
...
...
@@ -866,47 +978,211 @@ static int rec_to_file(unsigned short port, unsigned long limit)
int
wri_bytes
=
0
;
/* Bytes written */
int
src_len
=
sizeof
(
struct
sockaddr_storage
);
struct
sockaddr_storage
src_addr
;
struct
timeval
time1
;
struct
timeval
time1
,
tw1
;
struct
crude_struct
other_info
;
char
buffer
[
PMAXSIZE
];
struct
udp_data
*
rude_data
=
(
struct
udp_data
*
)
&
buffer
;
int
rude_seq
=
0
;
/* Initialize some variables */
memset
(
buffer
,
0
,
PMAXSIZE
);
other_info
.
dest_port
=
htons
(
port
);
while
(
1
)
{
// establish output file ring buffer
osize
=
sizeof
(
struct
udp_data
)
+
sizeof
(
struct
crude_struct
);
obufsize
=
OBUFFERS
*
osize
;
ochunksize
=
osize
*
OCHUNKS
;
obuffer
=
malloc
(
obufsize
);
if
(
opt_fork
)
main_output
=
fork_write
();
else
main_output
=
main_file
;
struct
pcap_pkthdr
*
header
;
const
u_char
*
packet
;
int
result
;
while
(
1
)
{
if
(
opt_pcap
){
if
(
(
result
=
pcap_next_ex
(
main_pcap
,
&
header
,
&
packet
))
<
1
){
if
(
result
==
0
)
continue
;
// just timeout waiting for data
fprintf
(
stderr
,
"crude: error when receiving pcap packet: result %d , error %s
\n
"
,
result
,
pcap_geterr
(
main_pcap
));
rec_bytes
=
0
;
};
rec_bytes
=
header
->
caplen
;
}
else
{
rec_bytes
=
recvfrom
(
main_socket
,
buffer
,
PMAXSIZE
,
0
,
(
struct
sockaddr
*
)
&
src_addr
,
(
socklen_t
*
)
&
src_len
);
if
(
rec_bytes
<=
0
)
}
if
(
rec_bytes
<=
0
)
{
RUDEBUG1
(
"crude: error when receiving packet: %s
\n
"
,
strerror
(
errno
));
RUDEBUG1
(
"crude: error when receiving packet: %s
\n
"
,
strerror
(
errno
));
}
else
else
{
gettimeofday
(
&
time1
,
NULL
);
if
(
opt_pcap
||
opt_af_packet
){
if
(
opt_pcap
)
{
// ether header
rude_data
=
(
struct
udp_data
*
)
(
packet
+
14
)
;
// feil : sizeof(struct ether_header);
memcpy
(
&
time1
,
&
header
->
ts
,
sizeof
(
struct
timeval
));
}
unsigned
short
iphdrlen
;
struct
iphdr
*
iph
=
(
struct
iphdr
*
)
rude_data
;
if
(
iph
->
protocol
==
17
){
// udp
iphdrlen
=
iph
->
ihl
*
4
;
struct
udphdr
*
udph
=
(
struct
udphdr
*
)((
char
*
)
rude_data
+
iphdrlen
);
if
(
ntohs
(
udph
->
dest
)
!=
port
)
continue
;
struct
sockaddr_in
*
v4
=
(
struct
sockaddr_in
*
)
&
(
src_addr
);
v4
->
sin_family
=
AF_INET
;
v4
->
sin_addr
=*
(
struct
in_addr
*
)
&
iph
->
saddr
;
/* turn around elsewhere
uint32_t v4adr=ntohl(iph->saddr);
memcpy( &(v4->sin_addr), &v4adr, sizeof(uint32_t)); */
v4
->
sin_port
=
port
;
}
else
{
continue
;
}
rude_data
=
(
struct
udp_data
*
)((
char
*
)
rude_data
+
iphdrlen
+
sizeof
(
struct
udphdr
));
}
if
(
opt_ktime
)
ioctl
(
main_socket
,
SIOCGSTAMP
,
&
time1
);
else
if
(
time1
.
tv_sec
==
0
)
gettimeofday
(
&
time1
,
NULL
);
int
newseq
=
ntohl
(
rude_data
->
sequence_number
);
if
(
rude_seq
>
0
&&
newseq
!=
(
rude_seq
+
1
)
){
fprintf
(
stderr
,
"Lost seq : %d - %d
\n
"
,
rude_seq
,
newseq
-
rude_seq
-
1
);
}
rude_seq
=
newseq
;
pkt_count
++
;
other_info
.
rx_time_seconds
=
htonl
(
time1
.
tv_sec
);
other_info
.
rx_time_useconds
=
htonl
(
time1
.
tv_usec
);
other_info
.
pkt_size
=
htonl
(
rec_bytes
);
//other_info.src_port = ((struct sockaddr_in6 *)&src_addr)->sin6_port;
//other_info.src_addr = ((struct sockaddr_in6 *)&src_addr)->sin6_addr;
other_info
.
src
=
src_addr
;
wri_bytes
=
write
(
main_file
,
buffer
,(
sizeof
(
struct
udp_data
)));
wri_bytes
+=
write
(
main_file
,
&
other_info
,
sizeof
(
struct
crude_struct
));
RUDEBUG7
(
"crude: pkt %lu (%ldbytes) status: %s
\n
"
,
pkt_count
,
rec_bytes
,
strerror
(
errno
));
other_info
.
src
=
src_addr
;
/* don't use network order here
struct sockaddr_in *sin_ref=(struct sockaddr_in*)&other_info.src;
struct sockaddr_in *src_ref=(struct sockaddr_in*)&src_addr;
sin_ref->sin_family=htons(AF_INET);
sin_ref->sin_port = htons(((struct sockaddr_in*)&src_addr)->sin_port);
uint32_t* h_a_r = (uint32_t*)&(src_ref->sin_addr);
uint32_t n_a = htonl(*h_a_r);
memcpy(&(sin_ref->sin_addr), &n_a, sizeof(struct in_addr));*/
// fill up buffer
int
newfree
=
ofree
+
osize
;
if
(
(
newfree
>
odata
&&
newfree
<=
obufsize
)
||
(
newfree
<
odata
)
){
memcpy
(
obuffer
+
ofree
,
rude_data
,
sizeof
(
struct
udp_data
));
memcpy
(
obuffer
+
ofree
+
sizeof
(
struct
udp_data
),
&
other_info
,
sizeof
(
struct
crude_struct
));
ofree
=
newfree
%
obufsize
;
}
else
{
perror
(
"Internal output buffer overflow
\n
"
);
}
// output data until empty or bad return
output_data
(
main_output
,
0
);
// RUDEBUG7("crude: pkt %lu (%lubytes) status: %s\n",
// pkt_count, rec_bytes, strerror(errno));
}
if
((
limit
!=
0
)
&&
(
pkt_count
>=
limit
)){
break
;
}
}
/* end of while */
}
/* end of while */
RUDEBUG1
(
"
\n
crude: captured/processed %lu packets
\n
"
,
pkt_count
);
return
0
;
}
static
int
fork_write
(
){
// fork a process to write to disk to avoid hangups
pid_t
child
=
0
;
int
recsz
=
sizeof
(
struct
udp_data
)
+
sizeof
(
struct
crude_struct
);
char
*
filebuffer
=
malloc
(
recsz
);
if
(
pipe
(
pipefd
)
==
-
1
)
// Create new pipe
{
fprintf
(
stderr
,
"Failed to create pipe
\n
"
);
exit
(
1
);
}
fcntl
(
pipefd
[
1
],
F_SETPIPE_SZ
,
1001001
);
fprintf
(
stderr
,
"pipe size is : %d
\n
"
,
fcntl
(
pipefd
[
1
],
F_GETPIPE_SZ
)
);
child
=
fork
();
if
(
child
){
// mother process
// set noblocking write to avoid buffer emptying wait.
if
(
fcntl
(
pipefd
[
1
],
F_SETFL
,
fcntl
(
pipefd
[
1
],
F_GETFL
,
0
)
|
O_NONBLOCK
)
!=
0
)
fprintf
(
stderr
,
"fcntl nonblock failed : %d
\n
"
,
errno
);
else
RUDEBUG1
(
"fcntl nonblock OK flag : %d
\n
"
,
fcntl
(
pipefd
[
1
],
F_GETFL
,
0
)
&
O_NONBLOCK
);
return
(
pipefd
[
1
]);
}
else
{
// in child - read and write to disk
close
(
pipefd
[
1
]);
// Child is read only
signal
(
SIGINT
,
SIG_IGN
);
// ignore signal - just wait for eof
int
bytes
;
while
(
(
bytes
=
read
(
pipefd
[
0
],
filebuffer
,
ochunksize
))
>
0
){
write
(
main_file
,
filebuffer
,
bytes
);
RUDEBUG7
(
"crude: writing to disk %u
\n
"
,
bytes
)
;
}
exit
(
0
);
}
}
// output data until empty or bad return
static
int
output_data
(
int
fd
,
int
flush
){
ssize_t
result
;
int
written
=
0
;
struct
timeval
tw1
,
tw2
;
while
(
ofree
!=
odata
){
int
ochunk
=
ofree
-
odata
;
if
(
ochunk
<
0
)
ochunk
=
obufsize
-
odata
;
if
(
flush
||
ochunk
>=
ochunksize
){
// delay writes until chunksize
// return(0);// just to see that write is not the problem #######################
if
(
ochunk
>
ochunksize
)
ochunk
=
ochunksize
;
gettimeofday
(
&
tw1
,
NULL
);
if
(
(
result
=
write
(
fd
,
obuffer
+
odata
,
ochunk
))
>
0
){
// IO buffer overflow
check_response
(
tw1
,
"write"
);
odata
=
(
odata
+
result
)
%
obufsize
;
written
+=
result
;
RUDEBUG1
(
"odata %lu ofree %d chunk %d result %d
\n
"
,
odata
,
ofree
,
ochunk
,
(
int
)
result
);
}
else
{
// I'll be back
check_response
(
tw1
,
"error"
);
if
(
result
==
EAGAIN
||
result
==
EWOULDBLOCK
){
RUDEBUG1
(
"Write failed wouldblock: reason %d
\n
"
,
(
int
)
result
);
break
;
// probably hit full ouput buffer
}
else
{
fprintf
(
stderr
,
"Write failed : return %d : cause %s
\n
"
,
(
int
)
result
,
strerror
(
errno
));
}
}
}
else
{
break
;
}
}
return
(
written
);
}
/* not used */
static
void
handle_interrupt
(
int
signum
){
output_data
(
pipefd
[
1
],
1
);
close
(
main_file
);
fprintf
(
stderr
,
"Cancelled by signal : %d"
,
signum
);
fflush
(
stderr
);
exit
(
1
);
}
/*
* get_str_addr - prints address in readable format into buffer
...
...
@@ -978,7 +1254,8 @@ static int rec_n_print(unsigned short port, unsigned long limit)
}
for
(
;
;
)
{
rec_bytes
=
recvmsg
(
main_socket
,
&
msgh
,
MSG_WAITALL
);
rec_bytes
=
recvmsg
(
main_socket
,
&
msgh
,
MSG_WAITALL
);
if
(
rec_bytes
<=
0
)
{
RUDEBUG1
(
"crude: error when receiving packet: %s
\n
"
,
strerror
(
errno
));
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment