PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Packet/Inject.pm

http://github.com/rramsden/TCP-IP-Stack
Perl | 524 lines | 392 code | 86 blank | 46 comment | 67 complexity | 7da057ea9c582859e1d5c43548125ec3 MD5 | raw file
  1. package Packet::Inject;
  2. #
  3. # $Id: Inject.pm,v 1.13 2002/05/02 08:22:21 cp5 Exp $
  4. use strict;
  5. use Packet::Definitions;
  6. use vars qw/ $VERSION @ISA /;
  7. $VERSION = '0.01';
  8. use Packet;
  9. @ISA = qw/ Packet /;
  10. sub new {
  11. my ($class, %args) = @_;
  12. my $self = {
  13. filter => 'bpf', # have to add function to find a filter automagically
  14. device => '', # have to add function to find a device automagically
  15. %args
  16. };
  17. return bless $self, ref($class) || $class;
  18. }
  19. # open methods
  20. sub open_bpf {
  21. my ($self) = @_;
  22. if (defined(&Packet::Definitions::BIOCGHDRCMPLT) && defined(&Packet::Definitions::BIOCSHDRCMPLT)) {
  23. $self->{spoof_eth_src} = pack("I", 1);
  24. }
  25. unless (opendir(DEV, "/dev")) {
  26. $self->{errbuf} = "opendir: /dev: $!\n";
  27. return 0;
  28. }
  29. my @bpf = grep { /^bpf/ } readdir(DEV);
  30. closedir(DEV);
  31. foreach (sort(@bpf)) {
  32. open($self->{fd}, "+</dev/$_") && last;
  33. }
  34. # Get BPF version
  35. my $bv;
  36. unless (ioctl($self->{fd}, &Packet::Definitions::BIOCVERSION, $bv)) {
  37. $self->{errbuf} = "BIOCVERSION (" . &Packet::Definitions::BIOCVERSION . "): $!";
  38. close($self->{fd});
  39. return 0;
  40. }
  41. my ($minor, $major) = unpack('SS', $bv);
  42. if ($major != &Packet::Definitions::BPF_MAJOR_VERSION || $minor < &Packet::Definitions::BPF_MINOR_VERSION) {
  43. $self->{errbuf} = "kernel bpf filter out of date";
  44. close($self->{fd});
  45. return 0;
  46. }
  47. # Attach network interface to bpf device
  48. my $ifr = pack('a16@48', $self->{device});
  49. unless (ioctl($self->{fd}, &Packet::Definitions::BIOCSETIF, $ifr)) {
  50. $self->{errbuf} = "$self->{device} (BIOCSETIF: " . &Packet::Definitions::BIOCSETIF . "): $!\n";
  51. close($self->{fd});
  52. return 0;
  53. }
  54. # Get the data link layer type
  55. my $v;
  56. unless (ioctl($self->{fd}, &Packet::Definitions::BIOCGDLT, $v)) {
  57. $self->{errbuf} = "BIOCGDLT: $!\n";
  58. close($self->{fd});
  59. return 0;
  60. }
  61. # NetBSD and FreeBSD BPF have an ioctl for enabling/disabling
  62. # automatic filling of the link level source address
  63. if (defined(&Packet::Definitions::BIOCGHDRCMPLT) && defined(&Packet::Definitions::BIOCSHDRCMPLT)) {
  64. unless (ioctl($self->{fd}, &Packet::Definitions::BIOCSHDRCMPLT, $self->{spoof_eth_src})) {
  65. $self->{errbuf} = "BIOCSHDRCMPLT: $!\n";
  66. close($self->{fd});
  67. return 0;
  68. }
  69. }
  70. # Assign link type and offset
  71. if ($v == &Packet::Definitions::DLT_SLIP) {
  72. $self->{linkoffset} = 0x10;
  73. }
  74. elsif ($v == &Packet::Definitions::DLT_RAW) {
  75. $self->{linkoffset} = 0x0;
  76. }
  77. elsif ($v == &Packet::Definitions::DLT_PPP) {
  78. $self->{linkoffset} = 0x04;
  79. }
  80. elsif ($v == &Packet::Definitions::DLT_EN10MB) {
  81. $self->{linkoffset} = 0xe;
  82. }
  83. else { # default to ethernet
  84. $self->{linkoffset} = 0xe;
  85. }
  86. if (defined(&Packet::Definitions::_BSDI_VERSION) && &Packet::Definitions::_BSDI_VERSION >= 199510) {
  87. if ($v == &Packet::Definitions::DLT_SLIP) {
  88. $v = &Packet::Definitions::DLT_SLIP_BSDOS;
  89. $self->{linkoffset} = 0x10;
  90. }
  91. elsif ($v == &Packet::Definitions::DLT_PPP) {
  92. $v = &Packet::Definitions::DLT_PPP_BSDOS;
  93. $self->{linkoffset} = 0x04;
  94. }
  95. }
  96. $self->{linktype} = $v;
  97. return 1;
  98. }
  99. sub open_nit {
  100. my ($self) = @_;
  101. unless (socket($self->{fd}, &Packet::Definitions::AF_INET, &Packet::Definitions::SOCK_RAW, &Packet::Definitions::NITPROTO_RAW)) {
  102. $self->{errbuf} = "socket: $!\n";
  103. return 0;
  104. }
  105. unless (bind($self->{fd}, &Packet::Definitions::sockaddr_nit(&Packet::Definitions::AF_INET, "", $self->{device}))) {
  106. $self->{errbuf} = "bind: $self->{device}: $!\n";
  107. close($self->{fd});
  108. return 0;
  109. }
  110. # NIT only supports Ethernet
  111. $self->{linktype} = &Packet::Definitions::DLT_EN10MB;
  112. return 1;
  113. }
  114. sub open_snit {
  115. my ($self) = @_;
  116. my $dev = "/dev/nit";
  117. unless (open($self->{fd}, "<+$dev")) {
  118. $self->{errbuf} = "open: $dev: $!\n";
  119. return 0;
  120. }
  121. # Arrange to get discrete messages from the STREAM and use NIT_BUF
  122. unless (ioctl($self->{fd}, &Packet::Definitions::I_SRDOPT, &Packet::Definitions::RMSGD)) {
  123. $self->{errbuf} = "I_SRDOPT: $!\n";
  124. close($self->{fd});
  125. return 0;
  126. }
  127. unless (ioctl($self->{fd}, &Packet::Definitions::I_PUSH, "nbuf")) {
  128. $self->{errbuf} = "push nbuf: $!\n";
  129. close($self->{fd});
  130. return 0;
  131. }
  132. # Request the interface
  133. my $ifr = &Packet::Definitions::ifreq($self->{device});
  134. $ifr =~ s/.$/ /;
  135. my $si = &Packet::Definitions::strioctl(&Packet::Definitions::NIOCBIND, "", &Packet::Definitions::sizeof("ifreq"), $ifr);
  136. unless (ioctl($self->{fd}, &Packet::Definitions::I_STR, $si)) {
  137. $self->{errbuf} = "NIOCBIND: $self->{device}: $!\n";
  138. close($self->{fd});
  139. return 0;
  140. }
  141. ioctl($self->{fd}, &Packet::Definitions::I_FLUSH, &Packet::Definitions::FLUSHR);
  142. # NIT only supports Ethernet
  143. $self->{linktype} = &Packet::Definitions::DLT_EN10MB;
  144. return 1;
  145. }
  146. sub open_sockpacket {
  147. my ($self) = @_;
  148. my $HAVE_PF_PACKET = 0;
  149. if (defined(&Packet::Definitions::PF_PACKET)) {
  150. $HAVE_PF_PACKET = 1;
  151. }
  152. if ($HAVE_PF_PACKET) {
  153. unless (socket(SOCK, &Packet::Definitions::PF_PACKET, &Packet::Definitions::SOCK_RAW, ?)) {
  154. $self->{errbuf} = "socket: $!\n";
  155. return 0;
  156. }
  157. }
  158. else {
  159. unless (socket(SOCK, &Packet::Definitions::PF_INET, &Packet::Definitions::SOCK_PACKET, ?)) {
  160. $self->{errbuf} = "socket: $!\n";
  161. return 0;
  162. }
  163. }
  164. if ($HAVE_PF_PACKET) {
  165. my $ifr = &Packet::Definitions::ifreq({ifr_name => "$self->{device}\0"});
  166. my $mr = &Packet::Definitions::unp_packet_mreq();
  167. unless (ioctl($self->{fd}, &Packet::Definitions::SIOCGIFINDEX, $ifr)) {
  168. $self->{errbuf} = "SIOCGIFINDEX $self->{device}: $!\n";
  169. close($self->{fd});
  170. return 0;
  171. }
  172. $ifr = &Packet::Definitions::unp_ifreq($ifr);
  173. $mr->{mr_ifindex} = $ifr->{ifr_ifindex};
  174. $mr->{mr_type} = &Packet::Definitions::PACKET_MR_ALLMULTI;
  175. $mr = &Packet::Definitions::packet_mreq($mr);
  176. unless (setsockopt($self->{fd}, &Packet::Definitions::SOL_PACKET, &Packet::Definitions::PACKET_ADD_MEMBERSHIP, $mr)) {
  177. $self->{errbuf} = "setsockopt $self->{device}: $!\n";
  178. close($self->{fd});
  179. return 0;
  180. }
  181. }
  182. my $ifr = &Packet::Definitions::ifreq({ifr_name => $self->{device}});
  183. unless (ioctl($self->{fd}, &Packet::Definitions::SIOCGIFHWADDR, $ifr)) {
  184. $self->{errbuf} = "SIOCGIFHWADDR: $!\n";
  185. close($self->{fd});
  186. return 0;
  187. }
  188. $ifr = &Packet::Definitions::unp_ifreq($ifr);
  189. if (
  190. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_ETHER ||
  191. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_METRICOM ||
  192. (
  193. defined(&Packet::Definitions::ARPHRD_LOOPBACK) &&
  194. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_LOOPBACK
  195. )
  196. ) {
  197. $self->{linktype} = &Packet::Definitions::DLT_EN10MB;
  198. $self->{linkoffset} = 0xe;
  199. }
  200. elsif (
  201. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_SLIP ||
  202. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_CSLIP ||
  203. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_SLIP6 ||
  204. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_CSLIP6 ||
  205. $ifr->{ifr_hwaddr}{sa_family} == &Packet::Definitions::ARPHRD_PPP
  206. ) {
  207. $self->{linktype} = &Packet::Definitions::DLT_RAW;
  208. }
  209. else {
  210. $self->{errbuf} = "unknown physical layer type 0x$ifr->{ifr_hwaddr}{sa_family}\n";
  211. close($self->{fd});
  212. return 0;
  213. }
  214. return 1;
  215. }
  216. # close methods
  217. sub close_bpf {
  218. my ($self) = @_;
  219. return (close($self->{fd}));
  220. }
  221. sub close_nit {
  222. my ($self) = @_;
  223. return (close($self->{fd}));
  224. }
  225. sub close_snit {
  226. my ($self) = @_;
  227. return (close($self->{fd}));
  228. }
  229. sub close_sockpacket {
  230. my ($self) = @_;
  231. return (close($self->{fd}));
  232. }
  233. # write methods
  234. sub write_bpf {
  235. my ($self, %args) = @_;
  236. my $bytes = 0;
  237. $args{times} = 1 unless $args{times};
  238. if ($args{length}) {
  239. while ($args{times}--) {
  240. $bytes += syswrite($self->{fd}, $args{packet}, $args{length});
  241. }
  242. }
  243. else {
  244. while ($args{times}--) {
  245. $bytes += syswrite($self->{fd}, $args{packet});
  246. }
  247. }
  248. return $bytes;
  249. }
  250. sub write_nit {
  251. my ($self, %args) = @_;
  252. my $bytes = 0;
  253. my $sockaddr = &Packet::Definitions::sockaddr({sa_data => $self->{device}});
  254. $args{times} = 1 unless $args{times};
  255. if ($args{length}) {
  256. while ($args{times}--) {
  257. $bytes += send($self->{fd}, $args{packet}, $args{length}, $sockaddr);
  258. }
  259. }
  260. else {
  261. while ($args{times}--) {
  262. $bytes += send($self->{fd}, $args{packet}, length($args{packet}), $sockaddr);
  263. }
  264. }
  265. return $bytes;
  266. }
  267. sub write_snit {
  268. my ($self, %args) = @_;
  269. my $bytes = 0;
  270. my $sockaddr = &Packet::Definitions::sockaddr({sa_data => $self->{device}});
  271. $args{times} = 1 unless $args{times};
  272. if ($args{length}) {
  273. while ($args{times}--) {
  274. $bytes += send($self->{fd}, $args{packet}, $args{length}, $sockaddr);
  275. }
  276. }
  277. else {
  278. while ($args{times}--) {
  279. $bytes += send($self->{fd}, $args{packet}, length($args{packet}), $sockaddr);
  280. }
  281. }
  282. return $bytes;
  283. }
  284. sub write_sockpacket {
  285. my ($self, %args) = 2_;
  286. my $sockaddr;
  287. my $bytes = 0;
  288. my $HAVE_PF_PACKET = 0;
  289. $args{times} = 1 unless $args{times};
  290. if (defined(&Packet::Definitions::PF_PACKET)) {
  291. $HAVE_PF_PACKET = 1;
  292. }
  293. if ($HAVE_PF_PACKET) {
  294. my $ifr = &Packet::Definitions::ifreq({ifr_name => "$self->{device}\0"});
  295. $sockaddr = &Packet::Definitions::unp_sockaddr_ll();
  296. unless (ioctl($self->{fd}, &Packet::Definitions::SIOCGIFINDEX, $ifr)) {
  297. $self->{errbuf} = "SIOCGIFINDEX $self->{device}: $!\n";
  298. close($self->{fd});
  299. return 0;
  300. }
  301. $ifr = &Packet::Definitions::unp_ifreq($ifr);
  302. $sockaddr->{sll_ifindex} = $ifr->{ifr_ifindex};
  303. $sockaddr->{sll_family} = &Packet::Definitions::AF_PACKET;
  304. $sockaddr->{sll_protocol} = pack("n", &Packet::Definitions::ETH_P_ALL);
  305. $sockaddr = &Packet::Definitions::sockaddr($sockaddr);
  306. }
  307. else {
  308. $sockaddr = &Packet::Definitions::sockaddr({sa_data => $self->{device}});
  309. }
  310. if ($args{length}) {
  311. while ($args{times}--) {
  312. $bytes += send($self->{fd}, $args{packet}, $args{length}, $sockaddr);
  313. }
  314. }
  315. else {
  316. while ($args{times}--) {
  317. $bytes += send($self->{fd}, $args{packet}, length($args{packet}), $sockaddr);
  318. }
  319. }
  320. return $bytes;
  321. }
  322. # this will be very cool when using the ::Defs::ifreq and unp_ifreq functions
  323. sub get_hwaddr_bpf {
  324. my ($self) = @_;
  325. my $len = pack('I', 0);
  326. my @mib = (
  327. &Packet::Definitions::CTL_NET,
  328. &Packet::Definitions::AF_ROUTE,
  329. 0,
  330. &Packet::Definitions::AF_LINK,
  331. &Packet::Definitions::NET_RT_IFLIST,
  332. 0
  333. );
  334. my $mib = pack('iiiiii', @mib);
  335. if (syscall(&Packet::Definitions::SYS___sysctl, $mib, 6, 0, $len, 0, 0) == -1) {
  336. return 0;
  337. }
  338. my $buf = pack('a' . unpack('I', $len), '');
  339. if (syscall(&Packet::Definitions::SYS___sysctl, $mib, 6, $buf, $len, 0, 0) == -1) {
  340. return 0;
  341. }
  342. my $tlen = unpack('I', $len);
  343. $buf =~ /^(.*?)\w{2,5}\d{1}/;
  344. my $val = (length($1) - 5);
  345. #struct if_msghdr {
  346. # u_short ifm_msglen; /* to skip over non-understood messages */
  347. # u_char ifm_version; /* future binary compatability */
  348. # u_char ifm_type; /* message type */
  349. # int ifm_addrs; /* like rtm_addrs */
  350. # int ifm_flags; /* value of if_flags */
  351. # u_short ifm_index; /* index for associated ifp */
  352. # struct if_data ifm_data;/* statistics and other data about if */
  353. #};
  354. my ($msglen, $nlen, $dv);
  355. for (my $i = 0; $i < $tlen; $i += $msglen) {
  356. ($msglen, $nlen, $dv) = unpack("S x$val C x2 a12", substr($buf, $i));
  357. my $devname = sprintf("%s", substr($dv, 0, $nlen));
  358. my $mac = sprintf("%x:%x:%x:%x:%x:%x", unpack('CCCCCC', substr($dv, $nlen, 6)));
  359. $self->{devs}{$devname} = $mac;
  360. }
  361. return 1;
  362. }
  363. # end = buf + len;
  364. # for (next = buf ; next < end ; next += ifm->ifm_msglen)
  365. # {
  366. # ifm = (struct if_msghdr *)next;
  367. # if (ifm->ifm_type == RTM_IFINFO)
  368. # {
  369. # sdl = (struct sockaddr_dl *)(ifm + 1);
  370. # if (strncmp(&sdl->sdl_data[0], device, sdl->sdl_nlen) == 0)
  371. # {
  372. # if (!(ea = malloc(sizeof(struct ether_addr))))
  373. # {
  374. # return (NULL);
  375. # }
  376. # memcpy(ea->ether_addr_octet, LLADDR(sdl), ETHER_ADDR_LEN);
  377. # break;
  378. # }
  379. # }
  380. # }
  381. # free(buf);
  382. # return (ea);
  383. # automagic functions
  384. sub open {
  385. my ($self) = @_;
  386. my $tmp = "open_$self->{filter}";
  387. return ($self->$tmp());
  388. }
  389. sub write {
  390. my ($self, %args) = @_;
  391. my $tmp = "write_$self->{filter}";
  392. return ($self->$tmp(%args));
  393. }
  394. sub close {
  395. my ($self) = @_;
  396. my $tmp = "close_$self->{filter}";
  397. return ($self->$tmp());
  398. }
  399. 1;
  400. __END__
  401. =head1 NAME
  402. Packet::Inject - portably inject raw Ethernet and IP packets
  403. =head1 SYNOPSIS
  404. use Packet::Inject;
  405. $i = Packet::Inject->new(
  406. device => 'eth0',
  407. filter => 'bpf'
  408. );
  409. $i->open();
  410. my $bytes = $i->write(
  411. packet => $raw_pkt,
  412. length => 28,
  413. times => 1,
  414. );
  415. $i->write(packet => $new_pkt);
  416. $i->close();
  417. print "total bytes sent: $bytes\n";
  418. print "packet dump:\n" . $i->hexdump($raw_ip) . "\n";
  419. =head1 DESCRIPTION
  420. Packet::Inject is a portable, *all Perl* module for injecting raw Ethernet and IP packets.
  421. =head1 FILTERS
  422. Packet::Inject supports:
  423. BPF (Berkeley Packet Filter) for *BSD, some Linux & Solaris systems, Darwin,
  424. SOCK_PACKET for Linux,
  425. NIT (Network Interface Tap) for SunOS 3,
  426. SNIT (STREAMS Network Interface Tap) for SunOS 4
  427. Filters to come:
  428. DLPI (Data Link Provider Interface) for Solaris, HP-UX, and SCO Openserver,
  429. PF (Packet Filter) for Tru64 UNIX,
  430. SNOOP for IRIX,
  431. LSF (Linux Socket Filter) for Linux kernels 2.1.75 and up
  432. =head1 AUTHORS
  433. Samy Kamkar <cp5@LucidX.com>
  434. Todd Caine <tcaine@eli.net>
  435. =head1 SEE ALSO
  436. Packet.pm
  437. Packet/Sniff.pm
  438. =cut