CARNIVORE

Version 9 (March 2024)

**Updated for Processing 4.3 and Silicon Macs**

Carnivore is a Processing library that allows you to perform surveillance on data networks. Carnivore listens to Internet traffic (email, web surfing, etc.) on a specific local network. Using Processing you are able to animate, diagnose, or interpret the network traffic in any way you wish.

@RadSoftGroup

Import as a Processing Library...



Requires Java and Processing

Installation Instructions

Documentation

FAQ

Carnivore is open source

Sample Code: A "Hello World" Client

Your browser does not support the canvas tag.

Download Code

// A Simple Carnivore Client -- print packets in Processing console
//
// Note: requires Carnivore Library for Processing (http://r-s-g.org/carnivore)
//
// + Windows:  first install winpcap (http://winpcap.org)
// + Mac:      first open a Terminal and execute this commmand: sudo chmod 777 /dev/bpf*
//             (must be done each time you reboot your mac)

import org.rsg.carnivore.*;
import org.rsg.lib.Log;

CarnivoreP5 c;

void setup(){
  size(600, 400);
  background(255);
  Log.setDebug(true); // Uncomment for verbose mode
  c = new CarnivoreP5(this); 
}

void draw(){}

// Called each time a new packet arrives
void packetEvent(CarnivorePacket p){
  println("(" + p.strTransportProtocol + " packet) " + 
  		  p.senderSocket() + " > " + p.receiverSocket());
  //println("Payload: " + p.ascii());
  //println("---------------------------\n");  		  
}

Sample Code: A Graphical Client

Your browser does not support the canvas tag.

Download Code

// A Less Simple Carnivore Client
//
// Note: requires Carnivore Library for Processing (http://r-s-g.org/carnivore)
//
// + Windows people:  first install winpcap (http://winpcap.org)
// + Mac people:      first open a Terminal and execute this commmand: sudo chmod 777 /dev/bpf*
//                    (must be done each time you reboot your mac)
//
// "offline" demo mode requires a log file created by CarnivorePE (see below) 
//

import java.util.Iterator;
import org.rsg.carnivore.*;
import org.rsg.carnivore.net.*;

// Flag for online/offline modes.
boolean isOnline = true; 

// For offline mode must have log file in sketch's "data" folder  
// You can make your own log file in CarnivorePE using the "headers only" channel 
String log_file = "Log_11Jan3a.txt";

HashMap nodes = new HashMap();
HashMap links = new HashMap();
float startDiameter = 120.0;
float halo_radius = 30;
int min_diameter = 70;
float startBrightness = 255.0;
float shrinkSpeed = 0.97;
float dimSpeed_link = 0.9;
float dimSpeed_ripple = 0.6;
String packets[];
PImage b;

color color1  = color(0, 0, 0, 170); 	        //0x000000 black	unknown
color color2  = color(153, 0, 204, 170); 	//0x9900CC purple	ftp
color color3  = color(51, 51, 204, 170); 	//0x3333CC darkblue   	itunes
color color4  = color(102, 102, 255, 170); 	//0x6666FF blue		http
color color5  = color(51, 102, 102, 170); 	//0x336666 darkgreen  	email
color color6  = color(102, 153, 0, 170); 	//0x669900 green	aim
color color7  = color(204, 255, 102, 170); 	//0xCCFF66 lightgreen 	Network Time Protocol
color color8  = color(255, 204, 0, 170); 	//0xFFCC00 tan		telnet/ssh
color color9  = color(255, 153, 0, 170); 	//0xFF9900 orange	BOOTP client
color color10 = color(255, 102, 102, 170); 	//0xFF6666 pink		netbios
color color11 = color(204, 0, 0, 170); 	        //0xCC0000 red		name-domain server

////////////////////////////////////////////////////////////////////////
class Node  {
  String ip;
  int port;
  float x, y, diameter; 
  color c;
  float ripple_color;
  int ripple_diameter;
  PImage icon; 
  float halo;

  Node(String ip, int port) {
    this.port = port;
    this.ip = ip;
    this.x = getXfromIP(ip);
    this.y = getYfromIP(ip);
    this.diameter = startDiameter;
    this.c = port2color(port);
    this.halo = halo_radius;
    this.ripple_color = 100;
    this.ripple_diameter = int(startDiameter + halo_radius);

    int r = int((x + y) % 4); //assign an icon that will be the same for the same x/y poss
    String icon_file = "cube.png";
    if(r == 0) { 
      icon_file = "cube.png"; 
    } else if(r == 1) { 
      icon_file = "message.png"; 
    } else if(r == 2) { 
      icon_file = "firefox.png"; 
    } else { 
      icon_file = "iTunes.png"; 
    }
    this.icon = loadImage(icon_file); // Load the images into the program
  }

  void shrink() {
    if(diameter > min_diameter) { 
      diameter = diameter * shrinkSpeed; 
      halo = halo * shrinkSpeed;
    }
    ripple_color = ripple_color * dimSpeed_ripple;
    ripple_diameter += 30;
  }

  void displayRipple() {
    if((ripple_diameter < (height*2)) && (ripple_color > 2)) { // avoid extreme cases
      stroke(255 - ripple_color);
      noFill();
      ellipse(x, y, ripple_diameter, ripple_diameter);  
    }
  }

  void display() {
    // Node circle
    stroke(0,0,0,100); // Circle rim
    fill(c); // Circle fill
    ellipse(x, y, diameter, diameter);

    // Halo
    noStroke();
    fill(color(100, 100, 100, 50));  // Halo
    ellipse(x, y, diameter + halo, diameter + halo);

    // Icon
    image(icon, x - icon.width/2, y - icon.height/2); 
  }
}

////////////////////////////////////////////////////////////////////////
class Link {
  String from_ip;
  String to_ip;
  float greyscale;
  int from_x, from_y, bez1_x, bez1_y, bez2_x, bez2_y, to_x, to_y;

  Link(String from_and_to) {
    this.from_ip = fromIPfromFromTo(from_and_to);
    this.to_ip = toIPfromFromTo(from_and_to);
    this.from_x = getXfromIP(from_ip);
    this.from_y = getYfromIP(from_ip);
    this.to_x = getXfromIP(to_ip);
    this.to_y = getYfromIP(to_ip);
    this.bez1_x = int((from_x + to_x)/2);
    this.bez1_y = from_y;   
    this.bez2_x = to_x;
    this.bez2_y = int((from_y + to_y)/2);   
    this.greyscale = startBrightness;
  }

  void dim() {
    greyscale = greyscale * dimSpeed_link;
  }

  void display() {
    stroke(int(255 - greyscale));
    noFill();
    bezier(from_x, from_y, bez1_x, bez1_y, bez2_x, bez2_y, to_x, to_y);
  }
}

////////////////////////////////////////////////////////////////////////
// Processing setup and draw loop
void setup(){
  size(625, 300);
  //size(625, 200);
  frameRate(10);
  smooth();
  ellipseMode(CENTER);

  b = loadImage("gradient.gif");

  //online mode
  if(isOnline) {
    CarnivoreP5 c = new CarnivoreP5(this);

  //offline mode
  } else {
    packets = loadStrings(log_file); // Need CarnivorePE "minivore" log file in "data" folder 
  }
}

void draw() {
  image(b, 0, 0);
  
  //stroke(100); noFill(); rect(0,0,width-1,height-1);

  if(!isOnline) {
    // Simulate incoming packets
    if(random(100) < 15) {
      String packet = packets[int(random(packets.length))]; // Get random packet from array of all packets
      String from_and_to = packet.substring(packet.indexOf(" ")+1);
      String from_ip = fromIPfromFromTo(from_and_to);
      int from_port = fromPortfromFromTo(from_and_to);
      String to_ip = toIPfromFromTo(from_and_to);
      int to_port = toPortfromFromTo(from_and_to);

      // Make Node objects and add them to the hashmap 
      nodes.put(from_ip, new Node(from_ip, from_port));
      nodes.put(to_ip, new Node(to_ip, to_port));

      //add packet to links
      links.put(from_and_to, new Link(from_and_to));
    }
  }

  drawRipples(); //draw these separate from the Nodes so they stay in rearground 
  drawLinks();
  drawNodes();
  //println("nodes:"+nodes.size() + " links:"+links.size());
}

// This is the callback for the online mode, i.e. Carnivore library triggers this method
synchronized void packetEvent(CarnivorePacket packet){
  if(isOnline) {
    println("[PDE] packetEvent: " + packet);

    // Make Node objects and add them to the hashmap 
    nodes.put(packet.senderAddress.toString(), new Node(packet.senderAddress.toString(), packet.senderPort));
    nodes.put(packet.receiverAddress.toString(), new Node(packet.receiverAddress.toString(), packet.receiverPort));

    //add packet to links
    String from_and_to = packet.senderSocket() + " > " + packet.receiverSocket();
    links.put(from_and_to, new Link(from_and_to));
  }
}

synchronized void drawNodes() {
  Iterator it = nodes.keySet().iterator();
  while(it.hasNext()){
    String ip = (String)it.next();
    Node n = (Node) nodes.get(ip);
    n.display();
    n.shrink();
  }  
}

synchronized void drawRipples() {
  Iterator it = nodes.keySet().iterator();
  while(it.hasNext()){
    String ip = (String)it.next();
    Node n = (Node) nodes.get(ip);
    n.displayRipple();
  }  
}

synchronized void drawLinks() {
  Iterator it = links.keySet().iterator();
  while(it.hasNext()){
    String from_and_to = (String)it.next();
    Link l = (Link) links.get(from_and_to);

    if(l.greyscale > 25) {
      l.display();
      l.dim();
    }
  }    
}

////////////////////////////////////////////////////////////////////////////
// Helper methods 
int getXfromIP(String ip) {
  // Use last two IP address bytes for x/y coords
  int splitter = ip.lastIndexOf(".");
  int y = int(ip.substring(splitter+1)) * height / 255; // Scale to applet size
  String tmp = ip.substring(0,splitter);
  splitter = tmp.lastIndexOf(".");
  int x = int(tmp.substring(splitter+1)) * width / 255; // Scale to applet size
  return x;
}

int getYfromIP(String ip) {
  // Use last two IP address bytes for x/y coords
  int splitter = ip.lastIndexOf(".");
  int y = int(ip.substring(splitter+1)) * height / 255; // Scale to applet size
  return y;
}


String fromIPfromFromTo(String from_and_to) {
  String from         = from_and_to.substring(0, from_and_to.indexOf(" > "));
  String to           = from_and_to.substring(from_and_to.indexOf(" > ")+3);
  String from_ip      = from.substring(0, from.indexOf(":"));
  return from_ip;
}

int fromPortfromFromTo(String from_and_to) {
  String from         = from_and_to.substring(0, from_and_to.indexOf(" > "));
  String to           = from_and_to.substring(from_and_to.indexOf(" > ")+3);
  String from_ip      = from.substring(0, from.indexOf(":"));
  String from_port    = from.substring(from.indexOf(":")+1);
  return int(from_port);
}

String toIPfromFromTo(String from_and_to) {
  String from         = from_and_to.substring(0, from_and_to.indexOf(" > "));
  String to           = from_and_to.substring(from_and_to.indexOf(" > ")+3);
  String from_ip      = from.substring(0, from.indexOf(":"));
  String from_port    = from.substring(from.indexOf(":")+1);
  String to_ip        = to.substring(0, to.indexOf(":"));
  return to_ip;
}

int toPortfromFromTo(String from_and_to) {
  String from         = from_and_to.substring(0, from_and_to.indexOf(" > "));
  String to           = from_and_to.substring(from_and_to.indexOf(" > ")+3);
  String from_ip      = from.substring(0, from.indexOf(":"));
  String from_port    = from.substring(from.indexOf(":")+1);
  String to_ip        = to.substring(0, to.indexOf(":"));
  String to_port      = to.substring(to.indexOf(":")+1);  
  return int(to_port);
}

boolean eitherPortMatches(String from_and_to, int p) {
  int from_port = fromPortfromFromTo(from_and_to);
  int to_port   = toPortfromFromTo(from_and_to);
  if((from_port == p) || (to_port == p)) {
    return true;
  }
  return false;
}

color port2color(int port) {
  if(port == 21)   { return color2  ; }   //ftp
  if(port == 22)   { return color8  ; }   //ssh
  if(port == 25)   { return color5  ; }   //smtp
  if(port == 53)   { return color11 ; }   //name-domain server
  if(port == 5353) { return color11 ; }   //name-domain server
  if(port == 68)   { return color9  ; }   //BOOTP client
  if(port == 69)   { return color9  ; }   //BOOTP client
  if(port == 80)   { return color4  ; }   //http
  if(port == 8020) { return color4  ; }   //http
  if(port == 443)  { return color4  ; }   //https
  if(port == 110)  { return color5  ; }   //pop3
  if(port == 123)  { return color7  ; }   //Network Time Protocol
  if(port == 137)  { return color10 ; }   //NETBIOS
  if(port == 138)  { return color10 ; }   //NETBIOS
  if(port == 139)  { return color10 ; }   //NETBIOS
  if(port == 427)  { return color3  ; }   //itunes?
  if(port == 5190) { return color6  ; }   //aim
  return color1;
  /*ADD THESE?
   	imap2		143/tcp		imap		# Interim Mail Access Proto
   	6346 gnutella / p2p
   	6348 gnutella / p2p
   	445 Samba
   	2222 (udp)	broadcasts Office on OSX  
   	*/
}