Class: Capp
- Inherits:
-
Object
- Object
- Capp
- Defined in:
- lib/capp.rb,
ext/capp/capp.c
Overview
Capp is a GVL-friendly libpcap wrapper library.
To create a packet capture device:
capp = Capp.live
This listens on the default device. You can list devices with Capp.devices.
To start capture use #loop:
capp.loop do |packet|
# ...
end
#loop yields a Capp::Packet object for each captured packet.
To stop capturing packets return (or break) from the loop, or call #stop on the Capp instance. You can resume capturing packets by calling #loop again after #stop.
To set a filter for only udp port 7647 (Rinda::RingFinger packets):
capp.filter = 'udp port 7647'
The format for a filter rule is the same as for tcpdump. See the pcap-filter(7) man page for the filter syntax.
You can use a Queue to capture packets in one thread and process them in another:
require 'capp'
require 'thread'
q = Queue.new
Thread.new do
while packet = q.deq do
# ...
end
end
capp = Capp.live.loop do |packet|
q.enq packet
end
Defined Under Namespace
Classes: Address, Device, Error, Packet, TestCase
Constant Summary collapse
- VERSION =
The version of Capp you are using
'1.0'
- ARPHRD_ETHER =
Ethernet hardware
INT2NUM(ARPHRD_ETHER)
- ARPHRD_FRELAY =
frame relay hardware
INT2NUM(ARPHRD_FRELAY)
- ARPHRD_IEEE1394 =
IEEE1394 (FireWire™) hardware
INT2NUM(ARPHRD_IEEE1394)
- ARPHRD_IEEE1394_EUI64 =
IEEE1394 (FireWire™) hardware with EUI-64 addresses
INT2NUM(ARPHRD_IEEE1394_EUI64)
- ARPHRD_IEEE802 =
token-ring hardware
INT2NUM(ARPHRD_IEEE802)
- ARPOP_INVREPLY =
ARP response identifying peer
INT2NUM(ARPOP_INVREPLY)
- ARPOP_INVREQUEST =
ARP request to identify peer
INT2NUM(ARPOP_INVREQUEST)
- ARPOP_REPLY =
ARP response to resolve request
INT2NUM(ARPOP_REPLY)
- ARPOP_REQUEST =
ARP resolve address request
INT2NUM(ARPOP_REQUEST)
- ARPOP_REVREPLY =
ARP response giving protocol address
INT2NUM(ARPOP_REVREPLY)
- ARPOP_REVREQUEST =
ARP request protocol address given hardware address
INT2NUM(ARPOP_REVREQUEST)
- DLT_NULL =
BSD loopback encapsulation.
INT2NUM(DLT_NULL)
- DLT_EN10MB =
Ethernet encapsulation.
INT2NUM(DLT_EN10MB)
- ETHERTYPE_ARP =
Address Resolution Protocol
INT2NUM(ETHERTYPE_ARP)
- ETHERTYPE_IP =
IPv4
INT2NUM(ETHERTYPE_IP)
- ETHERTYPE_IPV6 =
IPv6
INT2NUM(ETHERTYPE_IPV6)
- ETHERTYPE_LOOPBACK =
Used to test interfaces
INT2NUM(ETHERTYPE_LOOPBACK)
- ETHERTYPE_PUP =
PUP protocol
INT2NUM(ETHERTYPE_PUP)
- ETHERTYPE_PAE =
EAPOL PAE/802.1x
INT2NUM(ETHERTYPE_PAE)
- ETHERTYPE_REVARP =
Reverse Address Resolution Protocol
INT2NUM(ETHERTYPE_REVARP)
- ETHERTYPE_RSN_PREAUTH =
802.11i / RSN Pre-Authentication
INT2NUM(ETHERTYPE_RSN_PREAUTH)
- ETHERTYPE_VLAN =
IEEE 802.1Q VLAN tagging
INT2NUM(ETHERTYPE_VLAN)
- TCP_ACK =
TCP Acknowledged flag
INT2NUM(TH_ACK)
- TCP_CWR =
TCP Congestion Window Reduced flag
INT2NUM(TH_CWR)
- TCP_ECE =
TCP ECN echo flag
INT2NUM(TH_ECE)
- TCP_FIN =
TCP Finish flag
INT2NUM(TH_FIN)
- TCP_PUSH =
TCP Push flag
INT2NUM(TH_PUSH)
- TCP_RST =
TCP Reset flag
INT2NUM(TH_RST)
- TCP_SYN =
TCP Synchronize flag
INT2NUM(TH_SYN)
- TCP_URG =
TCP Urgent flag
INT2NUM(TH_URG)
Instance Attribute Summary collapse
-
#device ⇒ Object
readonly
Device name packets are being captured from.
Class Method Summary collapse
-
.default_device_name ⇒ String
Returns the default device name.
-
.devices ⇒ Array
Returns an Array containing the devices and their addresses:.
-
.drop_privileges(run_as_user, run_as_directory = nil) ⇒ Object
Drops root privileges to the given
run_as_user
and optionally chroots torun_as_directory
. -
.live(*args) ⇒ Object
Creates a Capp instance that will capture packets from a network device.
-
.offline(file) ⇒ Object
Creates an Capp that instance that captures packets from a pcap savefile.
-
.open(device_or_file, *args) ⇒ Object
Opens
device_or_file
as an offline device if it is an IO or an existing file. -
.pcap_lib_version ⇒ Object
Returns the libpcap version string:.
Instance Method Summary collapse
-
#datalink ⇒ Object
Returns datalink used for capturing packets.
-
#datalink=(datalink_name) ⇒ Object
Sets the link-layer header type to be used by the capture instance to the given
datalink_name
. -
#datalinks ⇒ Object
Returns the supported datalinks for this capture instance:.
-
#filter=(filter) ⇒ self
Sets the packet filter to the given
filter
string. -
#loop ⇒ Object
Starts capturing packets.
-
#promiscuous=(boolean) ⇒ Object
Enables or disables promiscuous mode.
-
#savefile_major_version ⇒ Integer
When called on a capture instance created from a savefile, returns the major version of the savefile.
-
#savefile_minor_version ⇒ Integer
When called on a capture instance created from a savefile, returns the minor version of the savefile.
-
#savefile_version ⇒ Object
When called on a capture instance created from a savefile, returns the version of the savefile.
-
#snaplen=(bytes) ⇒ Object
Sets the number of
bytes
captured from each packet. -
#stats ⇒ Hash
Retrieves packet capture statistics:.
-
#stop ⇒ Object
Stops a running loop.
-
#timeout=(milliseconds) ⇒ Object
Sets the maximum amount of time in
milliseconds
that will elapse between receiving a packet and yielding it to #loop.
Instance Attribute Details
#device ⇒ Object (readonly)
Device name packets are being captured from. Only set for live packet captures.
86 87 88 |
# File 'lib/capp.rb', line 86 def device @device end |
Class Method Details
.default_device_name ⇒ String
Returns the default device name
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'ext/capp/capp.c', line 124
static VALUE
capp_s_default_device_name(VALUE klass)
{
char errbuf[PCAP_ERRBUF_SIZE];
char *device;
*errbuf = '\0';
device = pcap_lookupdev(errbuf);
if (device == NULL)
rb_raise(eCappError, "pcap_create: %s", errbuf);
if (*errbuf)
rb_warn("%s", errbuf);
return rb_usascii_str_new_cstr(device);
}
|
.devices ⇒ Array
Returns an Array containing the devices and their addresses:
[#<struct Capp::Address
address="lo0",
netmask=nil,
broadcast=
[#<struct Capp::Address
address="0:0:0:0:0:0",
netmask=nil,
broadcast=nil,
destination=nil>,
#<struct Capp::Address
address="fe80::1%lo0",
netmask="ffff:ffff:ffff:ffff::",
broadcast=nil,
destination=nil>,
#<struct Capp::Address
address="127.0.0.1",
netmask="255.0.0.0",
broadcast=nil,
destination=nil>,
#<struct Capp::Address
address="::1",
netmask="ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
broadcast=nil,
destination=nil>],
destination=1>,
# [...]
]
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'ext/capp/capp.c', line 236
static VALUE
capp_s_devices(VALUE klass)
{
VALUE device, devices, dev_args[4];
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *iface, *ifaces;
*errbuf = '\0';
if (pcap_findalldevs(&ifaces, errbuf))
rb_raise(eCappError, "pcap_create: %s", errbuf);
if (*errbuf)
rb_warn("%s", errbuf);
devices = rb_ary_new();
for (iface = ifaces; iface; iface = iface->next) {
dev_args[0] = rb_usascii_str_new_cstr(iface->name);
if (iface->description) {
dev_args[1] = rb_usascii_str_new_cstr(iface->description);
} else {
dev_args[1] = Qnil;
}
dev_args[2] = capp_addr_to_addresses(iface->addresses);
dev_args[3] = UINT2NUM(iface->flags);
device = rb_class_new_instance(4, dev_args, cCappDevice);
rb_ary_push(devices, device);
}
pcap_freealldevs(ifaces);
return devices;
}
|
.drop_privileges(run_as_user, run_as_directory = nil) ⇒ Object
Drops root privileges to the given run_as_user
and optionally chroots to run_as_directory
. Use this method after creating a packet capture instance to improve security.
Returns true if privileges are dropped, raises a Capp::Error if privileges could not be dropped and returns a false value if there was no need to drop privileges.
You will be able to start and stop packet capture but not create new packet capture instances after dropping privileges.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/capp.rb', line 100 def self.drop_privileges run_as_user, run_as_directory = nil return unless Process.uid.zero? and Process.euid.zero? return unless run_as_user or run_as_directory raise Capp::Error, 'chroot without dropping root is insecure' if run_as_directory and not run_as_user require 'etc' begin pw = if Integer === run_as_user then Etc.getpwuid run_as_user else Etc.getpwnam run_as_user end rescue ArgumentError => e raise Capp::Error, "could not find user #{run_as_user}" end if run_as_directory then begin Dir.chroot run_as_directory Dir.chdir '/' rescue Errno::ENOENT => e raise Capp::Error, "could not chroot to #{run_as_directory} " + "or change to chroot directory" end end begin Process.gid = pw.gid Process.uid = pw.uid rescue Errno::EPERM => e raise Capp::Error, "unable to drop privileges to #{run_as_user} " + "(#{e.})" end true end |
.live ⇒ Object .device ⇒ Object .device ⇒ Object .device ⇒ Object .device ⇒ Object
Creates a Capp instance that will capture packets from a network device.
device
is the device to capture packets from. If the device is omitted the default device (::default_device_name) is used.
capture_length
is the number of bytes to capture from each packet. If a length is omitted 65535 is used.
promiscuous
places the device in promiscuous mode when true, allowing you to see packets not sent directly to or from the device. Promiscuous mode is enabled by default.
The timeout
is the number of maximum number of milliseconds that will elapse between receiving a packet and yielding it to the block given to #loop. The default timeout is 10 milliseconds. See #timeout= for further discussion.
After creating an instance use #loop to start capturing packets.
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'ext/capp/capp.c', line 300
static VALUE
capp_s_open_live(int argc, VALUE *argv, VALUE klass)
{
VALUE obj, device, snaplen, promiscuous, timeout;
int promisc = 0;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
rb_scan_args(argc, argv, "04", &device, &snaplen, &promiscuous, &timeout);
if (!RTEST(device)) device = capp_s_default_device_name(klass);
if (!RTEST(snaplen)) snaplen = INT2NUM(65535);
if (!RTEST(promiscuous)) promiscuous = Qtrue;
if (!RTEST(timeout)) timeout = INT2NUM(10);
if (RTEST(promiscuous))
promisc = 1;
*errbuf = '\0';
handle = pcap_open_live(StringValueCStr(device), NUM2INT(snaplen),
promisc, NUM2INT(timeout), errbuf);
if (NULL == handle)
rb_raise(eCappError, "pcap_create: %s", errbuf);
if (*errbuf)
rb_warn("%s", errbuf);
obj = Data_Wrap_Struct(klass, NULL, pcap_close, handle);
rb_ivar_set(obj, id_iv_device, device);
return obj;
}
|
.filename ⇒ Object .io ⇒ Object
Creates an Capp that instance that captures packets from a pcap savefile. A savefile may be loaded from an open file
:
open 'savefile' do |io|
capp = Capp.offline io
# ...
end
Or a filename
:
capp = Capp.offline 'savefile'
After creating an instance use #loop to start capturing packets.
NOTE: When you give Capp.offline a Ruby IO you should avoid buffered reads or writes to the IO due to limitations of libpcap. (Using a pipe and reading from one end and writing to the other is fine, of course.)
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'ext/capp/capp.c', line 359
static VALUE
capp_s_open_offline(VALUE klass, VALUE file)
{
VALUE obj;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
*errbuf = '\0';
if (TYPE(file) == T_FILE) {
FILE *c_file;
rb_io_t *fptr;
int fd;
GetOpenFile(file, fptr);
fd = dup(fptr->fd);
if (-1 == fd)
rb_sys_fail("dup");
c_file = fdopen(fd, "r");
if (NULL == c_file)
rb_sys_fail("fdopen");
handle = pcap_fopen_offline(c_file, errbuf);
} else {
handle = pcap_open_offline(StringValueCStr(file), errbuf);
}
if (NULL == handle) {
if (RFILE(file))
rb_raise(eCappError, "pcap_fopen_offline: %s", errbuf);
rb_raise(eCappError, "pcap_open_offline: %s", errbuf);
}
if (*errbuf)
rb_warn("%s", errbuf);
obj = Data_Wrap_Struct(klass, NULL, pcap_close, handle);
rb_ivar_set(obj, id_iv_device, Qnil);
return obj;
}
|
.open(device_or_file, *args) ⇒ Object
Opens device_or_file
as an offline device if it is an IO or an existing file. args
are ignored (as ::offline does not support any).
Opens device_or_file
as a live device otherwise, along with args
. See ::live for documentation on the additional arguments.
147 148 149 150 151 152 153 |
# File 'lib/capp.rb', line 147 def self.open device_or_file, *args if IO === device_or_file or File.exist? device_or_file then offline device_or_file, *args else live device_or_file, *args end end |
.pcap_lib_version ⇒ Object
Returns the libpcap version string:
Capp.pcap_lib_version #=> "libpcap version 1.1.1"
416 417 418 419 420 |
# File 'ext/capp/capp.c', line 416
static VALUE
capp_s_pcap_lib_version(VALUE klass)
{
return rb_usascii_str_new_cstr(pcap_lib_version());
}
|
Instance Method Details
#datalink ⇒ Object
472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'ext/capp/capp.c', line 472
static VALUE
capp_datalink(VALUE self)
{
const char *datalink_name;
int dlt;
pcap_t *handle;
GetCapp(self, handle);
dlt = pcap_datalink(handle);
datalink_name = pcap_datalink_val_to_name(dlt);
return rb_usascii_str_new_cstr(datalink_name);
}
|
#datalink=(datalink_name) ⇒ Object
Sets the link-layer header type to be used by the capture instance to the given datalink_name
. You can see the supported datalink names by calling #datalinks on the capture instance.
Note that most possible datalink types do not have full support in Capp. You may receive the raw packet without any further extraction of packet fields.
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
# File 'ext/capp/capp.c', line 950
static VALUE
capp_set_datalink(VALUE self, VALUE datalink)
{
int dlt;
pcap_t *handle;
const char *datalink_name = StringValueCStr(datalink);
GetCapp(self, handle);
dlt = pcap_datalink_name_to_val(datalink_name);
if (-1 == dlt)
rb_raise(eCappError, "unrecognized datalink name %s", datalink_name);
if (pcap_set_datalink(handle, dlt))
rb_raise(eCappError, "%s", pcap_geterr(handle));
return datalink;
}
|
#datalinks ⇒ Object
Returns the supported datalinks for this capture instance:
p Capp.live.datalinks
#=> ["EN10MB", "PPI", "IEEE802_11_RADIO", "IEEE802_11",
"IEEE802_11_RADIO_AVS", "RAW"]
These can be used to change the datalink used to capture packets by using #datalink=
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'ext/capp/capp.c', line 435
static VALUE
capp_datalinks(VALUE self)
{
int *dlt_buf;
pcap_t *handle;
VALUE datalink_ary;
int i, datalink_count;
GetCapp(self, handle);
datalink_count = pcap_list_datalinks(handle, &dlt_buf);
if (datalink_count == -1)
rb_raise(eCappError, "%s", pcap_geterr(handle));
datalink_ary = rb_ary_new2(datalink_count);
for (i = 0; i < datalink_count; i++) {
const char *datalink_name_cstr = pcap_datalink_val_to_name(dlt_buf[i]);
VALUE datalink_name = rb_usascii_str_new_cstr(datalink_name_cstr);
rb_ary_push(datalink_ary, datalink_name);
}
pcap_free_datalinks(dlt_buf);
return datalink_ary;
}
|
#filter=(filter) ⇒ self
Sets the packet filter to the given filter
string. The format is the same format as for tcpdump. Read the pcap-filter(7) man page for documentation on the filter syntax.
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
# File 'ext/capp/capp.c', line 978
static VALUE
capp_set_filter(VALUE self, VALUE filter)
{
VALUE device;
pcap_t *handle;
struct bpf_program program;
bpf_u_int32 network, netmask = PCAP_NETMASK_UNKNOWN;
char errbuf[PCAP_ERRBUF_SIZE];
int res;
device = rb_ivar_get(self, id_iv_device);
if (RTEST(device)) {
*errbuf = '\0';
res =
pcap_lookupnet(StringValueCStr(device), &network, &netmask, errbuf);
if (res == -1)
rb_raise(eCappError, "%s", errbuf);
if (*errbuf)
rb_warn("%s", errbuf);
}
GetCapp(self, handle);
res = pcap_compile(handle, &program, StringValueCStr(filter), 0, netmask);
if (res)
rb_raise(eCappError, "%s", pcap_geterr(handle));
res = pcap_setfilter(handle, &program);
pcap_freecode(&program);
if (res)
rb_raise(eCappError, "%s", pcap_geterr(handle));
return self;
}
|
#loop {|packet| ... } ⇒ self #loop ⇒ Object
Starts capturing packets. Each packet captured is yielded to the block. Packets are instances of Capp::Packet.
If no block is given an enumerator is returned.
You can stop capturing packets by returning from the block (or using break) or by calling #stop on the instance. Packet capture can be restarted later.
892 893 894 895 896 897 898 899 900 |
# File 'ext/capp/capp.c', line 892
static VALUE
capp_loop(VALUE self)
{
RETURN_ENUMERATOR(self, 0, 0);
rb_ensure(capp_loop_run, self, capp_loop_end, self);
return self;
}
|
#promiscuous=(boolean) ⇒ Object
Enables or disables promiscuous mode. When promiscuous mode is enabled packets that were not sent directly to the device will be captured.
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 |
# File 'ext/capp/capp.c', line 1027
static VALUE
capp_set_promisc(VALUE self, VALUE promiscuous)
{
pcap_t *handle;
int promisc = RTEST(promiscuous);
GetCapp(self, handle);
if (pcap_set_promisc(handle, promisc))
rb_raise(eCappError, "pcap already activated");
return promiscuous;
}
|
#savefile_major_version ⇒ Integer
When called on a capture instance created from a savefile, returns the major version of the savefile. When called on a live capture instance it returns a meaningless value.
910 911 912 913 914 915 916 917 918 |
# File 'ext/capp/capp.c', line 910
static VALUE
capp_savefile_major_version(VALUE self)
{
pcap_t *handle;
GetCapp(self, handle);
return INT2NUM(pcap_major_version(handle));
}
|
#savefile_minor_version ⇒ Integer
When called on a capture instance created from a savefile, returns the minor version of the savefile. When called on a live capture instance it returns a meaningless value.
928 929 930 931 932 933 934 935 936 |
# File 'ext/capp/capp.c', line 928
static VALUE
capp_savefile_minor_version(VALUE self)
{
pcap_t *handle;
GetCapp(self, handle);
return INT2NUM(pcap_minor_version(handle));
}
|
#savefile_version ⇒ Object
When called on a capture instance created from a savefile, returns the version of the savefile. When called on a live capture instance it returns a meaningless value.
160 161 162 |
# File 'lib/capp.rb', line 160 def savefile_version "#{savefile_major_version}.#{savefile_minor_version}" end |
#snaplen=(bytes) ⇒ Object
Sets the number of bytes
captured from each packet.
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 |
# File 'ext/capp/capp.c', line 1047
static VALUE
capp_set_snaplen(VALUE self, VALUE snaplen)
{
pcap_t *handle;
GetCapp(self, handle);
if (pcap_set_snaplen(handle, NUM2INT(snaplen)))
rb_raise(eCappError, "pcap already activated");
return snaplen;
}
|
#stats ⇒ Hash
Retrieves packet capture statistics:
p capp.stats #=> {:drop => 0, :ifdrop => 0, :recv => 123}
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 |
# File 'ext/capp/capp.c', line 1096
static VALUE
capp_stats(VALUE self)
{
VALUE stats;
pcap_t *handle;
struct pcap_stat ps;
GetCapp(self, handle);
if (pcap_stats(handle, &ps))
rb_raise(eCappError, "%s", pcap_geterr(handle));
stats = rb_hash_new();
rb_hash_aset(stats, ID2SYM(id_drop), UINT2NUM(ps.ps_drop));
rb_hash_aset(stats, ID2SYM(id_ifdrop), UINT2NUM(ps.ps_ifdrop));
rb_hash_aset(stats, ID2SYM(id_recv), UINT2NUM(ps.ps_recv));
return stats;
}
|
#stop ⇒ Object
Stops a running loop
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 |
# File 'ext/capp/capp.c', line 1123
static VALUE
capp_stop(VALUE self)
{
pcap_t *handle;
GetCapp(self, handle);
pcap_breakloop(handle);
return self;
}
|
#timeout=(milliseconds) ⇒ Object
Sets the maximum amount of time in milliseconds
that will elapse between receiving a packet and yielding it to #loop.
Reducing the timeout will increase responsiveness as pcap_loop(3) must “check in” more frequently while increasing the timeout will reduce responsiveness.
Setting the timeout too low may increase GVL contention when many packets are arriving at once as #loop will be waking up frequently to service captured packets.
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 |
# File 'ext/capp/capp.c', line 1075
static VALUE
capp_set_timeout(VALUE self, VALUE milliseconds)
{
pcap_t *handle;
GetCapp(self, handle);
if (pcap_set_timeout(handle, NUM2INT(milliseconds)))
rb_raise(eCappError, "pcap already activated");
return milliseconds;
}
|