├── .gitignore ├── Cargo.toml ├── README.md ├── d3cap.png └── src ├── client ├── client.css ├── client.html └── client.js ├── d3cap ├── cli.rs ├── d3cap.rs ├── dot11.rs ├── ether.rs ├── ip.rs ├── main.rs ├── pkt_graph.rs ├── readline.rs ├── tap.rs └── util.rs ├── fixed_ring ├── Cargo.toml └── src │ └── lib.rs ├── json_serve ├── Cargo.toml └── src │ ├── lib.rs │ ├── rustwebsocket.rs │ └── uiserver.rs ├── multicast ├── Cargo.toml └── src │ └── lib.rs └── pcap ├── Cargo.toml └── src ├── lib.rs ├── pcap.rs └── pcapll.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.dSYM 3 | target/ 4 | sampledump.pcap 5 | conf.toml 6 | TAGS 7 | .cargo 8 | .idea/ 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "d3cap" 4 | version = "0.0.1" 5 | authors = [ "jfager@gmail.com" ] 6 | 7 | 8 | [[bin]] 9 | 10 | name = "d3cap" 11 | path = "src/d3cap/main.rs" 12 | 13 | [dependencies] 14 | 15 | time = "*" 16 | getopts = "*" 17 | rustc-serialize = "*" 18 | bitflags = "*" 19 | clippy = "*" 20 | toml = "*" 21 | 22 | 23 | [dependencies.pcap] 24 | 25 | path = "src/pcap" 26 | 27 | 28 | [dependencies.multicast] 29 | 30 | path = "src/multicast" 31 | 32 | 33 | [dependencies.fixed_ring] 34 | 35 | path = "src/fixed_ring" 36 | 37 | 38 | [dependencies.json_serve] 39 | 40 | path = "src/json_serve" 41 | 42 | 43 | # [dependencies.rustc-serialize] 44 | 45 | # git = "https://github.com/rust-lang/rustc-serialize.git" 46 | 47 | 48 | [profile.dev] 49 | debug = false 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # d3cap 2 | 3 | A [libpcap]-based network activity visualizer / playground for learning [d3] and [Rust]. 4 | 5 | [libpcap]: http://www.tcpdump.org/ 6 | [d3]: http://d3js.org/ 7 | [rust]: http://www.rust-lang.org/ 8 | 9 | Includes half-assed rust bindings for libpcap and a quarter-assed rust websocket server implementation. 10 | 11 | To try it out, you'll need libpcap installed and a recent rust compiler. Once these are set up, clone the project and use [cargo] to build, like so: 12 | 13 | [cargo]: http://crates.io 14 | 15 | $ git clone https://github.com/jfager/d3cap.git 16 | $ cd d3cap 17 | $ cargo build 18 | 19 | The resulting binary ends up in target/d3cap. Run this and open src/client/client.html in a browser and hit the Connect button, and you should start seeing network activity pop up: 20 | 21 | ![](https://raw.github.com/jfager/d3cap/master/d3cap.png "d3cap") 22 | 23 | The size of each node indicates how much data has passed through the corresponding host, with blue and orange showing the proportion sent and received. You can mouse over a node to see the corresponding address. 24 | -------------------------------------------------------------------------------- /d3cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jfager/d3cap/d53197fb538f88c163038a6be5b363b87b4e7883/d3cap.png -------------------------------------------------------------------------------- /src/client/client.css: -------------------------------------------------------------------------------- 1 | .ip4 { 2 | color: 'green'; 3 | } 4 | 5 | .connectForm:before { 6 | content: ''; 7 | display: inline-block; 8 | height: 100%; 9 | vertical-align: bottom; 10 | } 11 | 12 | #connectForm { 13 | vertical-align: bottom; 14 | } 15 | 16 | .node:not(:hover) .nodetext { 17 | display: none; 18 | } 19 | 20 | .node:not(:hover) .knownaddr { 21 | display: initial; 22 | } 23 | 24 | td { 25 | padding: 10px; 26 | } 27 | 28 | /* For d3 force demo */ 29 | .link { 30 | stroke: #ccc; 31 | } 32 | 33 | .node text { 34 | pointer-events: none; 35 | font: 10px sans-serif; 36 | } 37 | /* End d3 force demo */ 38 | 39 | 40 | 41 | /* Left tabs for bootstrap 3, taken from 42 | * http://stackoverflow.com/questions/18432577/stacked-tabs-in-bootstrap-3 43 | */ 44 | .tabs-left > .nav-tabs { 45 | border-bottom: 0; 46 | } 47 | 48 | .tab-content > .tab-pane { 49 | display: none; 50 | } 51 | 52 | .tab-content > .active { 53 | display: block; 54 | } 55 | 56 | .tabs-left > .nav-tabs > li { 57 | float: none; 58 | } 59 | 60 | .tabs-left > .nav-tabs > li > a { 61 | min-width: 74px; 62 | margin-right: 0; 63 | margin-bottom: 3px; 64 | } 65 | 66 | .tabs-left > .nav-tabs { 67 | float: left; 68 | margin-right: 19px; 69 | border-right: 1px solid #ddd; 70 | } 71 | 72 | .tabs-left > .nav-tabs > li > a { 73 | margin-right: -1px; 74 | -webkit-border-radius: 4px 0 0 4px; 75 | -moz-border-radius: 4px 0 0 4px; 76 | border-radius: 4px 0 0 4px; 77 | } 78 | 79 | .tabs-left > .nav-tabs > li > a:hover, 80 | .tabs-left > .nav-tabs > li > a:focus { 81 | border-color: #eeeeee #dddddd #eeeeee #eeeeee; 82 | } 83 | 84 | .tabs-left > .nav-tabs .active > a, 85 | .tabs-left > .nav-tabs .active > a:hover, 86 | .tabs-left > .nav-tabs .active > a:focus { 87 | border-color: #ddd transparent #ddd #ddd; 88 | *border-right-color: #ffffff; 89 | } 90 | -------------------------------------------------------------------------------- /src/client/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | d3cap 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 |

d3cap

13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 |
21 |
22 |
23 |
24 |
25 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/client/client.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | var ws; 4 | var macAddrMap; 5 | 6 | function mkForce(nodes, links, width, height) { 7 | return d3.layout.force() 8 | .nodes(nodes) 9 | .links(links) 10 | .gravity(0.05) 11 | .distance(function(l) { return 30 + 1.5 * (l.source.displaySize + 12 | l.target.displaySize); }) 13 | .charge(function(d) { return -100 - d.displaySize; }) 14 | .size([width, height]); 15 | } 16 | 17 | //TODO: this shouldn't append svg, leave that to the specific tabs 18 | function mkTab(tabId, tabText, active) { 19 | var tabTop = d3.select('#force-graph-tabs') 20 | .append("li") 21 | .append("a").attr("href", "#"+tabId).attr("data-toggle", "tab") 22 | .text(tabText); 23 | var tabCont = d3.select('#force-graph-contents') 24 | .append("div").attr("id", tabId); 25 | 26 | if(active) { 27 | tabTop.attr("class", "active"); 28 | tabCont.attr("class", "tab-pane active"); 29 | } else { 30 | tabCont.attr("class", "tab-pane"); 31 | } 32 | 33 | return tabCont; 34 | } 35 | 36 | function mkConnsTab(tabId, type, active) { 37 | var boundary = $('.tab-content'); 38 | var width = boundary[0].offsetWidth - 200; 39 | var height = width * 0.66; 40 | return mkTab(tabId, type, active) 41 | .append("svg").attr("width", width) 42 | .attr("height", height); 43 | } 44 | 45 | function mkConns(type, active) { 46 | var tabId = "tab_"+type; 47 | var chart = mkConnsTab(tabId, type, active); 48 | 49 | var width = chart.attr("width"); 50 | var height = chart.attr("height"); 51 | var nodes = [], links = []; 52 | 53 | var force = mkForce(nodes, links, width, height); 54 | force.on("tick", function() { 55 | chart.selectAll(".link") 56 | .attr("x1", function(d) { return d.source.x; }) 57 | .attr("y1", function(d) { return d.source.y; }) 58 | .attr("x2", function(d) { return d.target.x; }) 59 | .attr("y2", function(d) { return d.target.y; }); 60 | chart.selectAll(".node") 61 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); 62 | }); 63 | 64 | return { 65 | nodes: nodes, 66 | nodeMap: {}, 67 | links: links, 68 | linkNodes: {}, 69 | chart: chart, 70 | force: force 71 | }; 72 | } 73 | 74 | function cmp() { 75 | for(var i=0; i b) { 81 | return 1; 82 | } 83 | } 84 | return 0; 85 | } 86 | 87 | function displaySize(size) { 88 | return 2 + Math.sqrt(size / Math.PI) / 120; 89 | } 90 | 91 | var types = { 92 | 'ip4': mkConns('ip4', true), 93 | 'ip6': mkConns('ip6', false), 94 | 'mac': mkConns('mac', false) 95 | }; 96 | 97 | var pie = d3.layout.pie() 98 | .value(function(d) { return d.sz; }) 99 | .sort(null); 100 | 101 | var color = d3.scale.category10(); 102 | 103 | var update = function(c) { 104 | c.force.start(); 105 | c.chart.selectAll(".link") 106 | .data(c.links) 107 | .enter().insert("line", "g") 108 | .attr("class", "link") 109 | .style("stroke-width", function(d) { return Math.sqrt(d.value); }); 110 | 111 | var nodes = c.chart.selectAll(".node").data(c.nodes); 112 | var newNodes = nodes.enter() 113 | .append("svg:g") 114 | .attr("class", "node") 115 | .call(c.force.drag); 116 | 117 | //update size for all nodes, not just new ones. 118 | var arcs = nodes.selectAll(".slice") 119 | .data(function(d) { return pie([{r:d.displaySize, sz: d.sizeFrom}, 120 | {r:d.displaySize, sz: d.sizeTo}]); }); 121 | 122 | arcs.enter() 123 | .append("svg:path") 124 | .attr("class", "slice") 125 | .attr("fill", function(d, i) { return color(i); }); 126 | 127 | arcs.attr("d", function(d) { 128 | return d3.svg.arc() 129 | .innerRadius(d.data.r * 0.4) 130 | .outerRadius(d.data.r)(d); 131 | }); 132 | 133 | newNodes.append("svg:text") 134 | .attr("class", function(d) { 135 | var alias = macAddrMap[d.addr]; 136 | return "nodetext" + (alias ? " knownaddr" : ""); 137 | }) 138 | .attr("dx", 12) 139 | .attr("dy", ".35em") 140 | .text(function(d) { 141 | var alias = macAddrMap[d.addr]; 142 | return alias ? alias : d.addr; 143 | }); 144 | 145 | }; 146 | 147 | function updateNode(c, from, to) { 148 | var updateLinks = false; 149 | var index = c.nodeMap[from.addr]; 150 | if(index === undefined) { 151 | index = c.nodes.length; 152 | c.nodes.push({addr: from.addr, 153 | countFrom: from.sent.count, 154 | sizeFrom: from.sent.size, 155 | countTo: to.sent.count, 156 | sizeTo: to.sent.size, 157 | displaySize: displaySize(from.sent.size+to.sent.size)}); 158 | c.nodeMap[from.addr] = index; 159 | updateLinks = true; 160 | } else { 161 | var node = c.nodes[index]; 162 | node.countFrom += from.sent.count; 163 | node.sizeFrom += from.sent.size; 164 | node.countTo += to.sent.count; 165 | node.sizeTo += to.sent.size; 166 | node.displaySize = displaySize(node.sizeFrom+node.sizeTo); 167 | } 168 | return updateLinks; 169 | } 170 | 171 | function loadUpdate(msg) { 172 | var c = types[msg.typ]; 173 | if(!c) { 174 | return; 175 | } 176 | 177 | var route = msg.route; 178 | 179 | var linkKey = route.a.addr+"_"+route.b.addr; 180 | var oldLinkNode = c.linkNodes[linkKey]; 181 | if(oldLinkNode) { 182 | var oldA = c.nodes[c.nodeMap[oldLinkNode.a.addr]]; 183 | oldA.countFrom -= oldLinkNode.a.sent.count; 184 | oldA.sizeFrom -= oldLinkNode.a.sent.size; 185 | oldA.countTo -= oldLinkNode.b.sent.count; 186 | oldA.sizeTo -= oldLinkNode.b.sent.size; 187 | 188 | var oldB = c.nodes[c.nodeMap[oldLinkNode.b.addr]]; 189 | oldB.countFrom -= oldLinkNode.b.sent.count; 190 | oldB.sizeFrom -= oldLinkNode.b.sent.size; 191 | oldB.countTo -= oldLinkNode.a.sent.count; 192 | oldB.sizeTo -= oldLinkNode.a.sent.size; 193 | } 194 | c.linkNodes[linkKey] = route; 195 | 196 | //bitwise-or to avoid short-circuit 197 | var updateLinks = updateNode(c, route.a, route.b) | updateNode(c, route.b, route.a); 198 | 199 | if(updateLinks) { 200 | c.links.push({source: c.nodeMap[route.a.addr], 201 | target: c.nodeMap[route.b.addr]}); 202 | } 203 | 204 | update(c); 205 | } 206 | 207 | $('#connectForm').on('submit', function() { 208 | ws = new WebSocket($('#wsServer').val()); 209 | ws.onopen = function() { 210 | console.log("websocket opened"); 211 | $('#wsServer').attr('disabled', 'disabled'); 212 | $('#connect').attr('disabled', 'disabled'); 213 | $('#disconnect').removeAttr('disabled'); 214 | $('#message').removeAttr('disabled').focus(); 215 | $('#send').removeAttr('disabled'); 216 | }; 217 | 218 | ws.onerror = function() { 219 | console.log("websocket error"); 220 | ws.close(); 221 | }; 222 | 223 | ws.onmessage = function(event) { 224 | var msg = JSON.parse(event.data); 225 | //console.log(msg); 226 | if(msg.typ === undefined) { 227 | macAddrMap = msg; 228 | } else { 229 | loadUpdate(msg); 230 | } 231 | }; 232 | 233 | ws.onclose = function() { 234 | console.log("websocket closed"); 235 | $('#wsServer').removeAttr('disabled'); 236 | $('#connect').removeAttr('disabled'); 237 | $('#disconnect').attr('disabled', 'disabled'); 238 | $('#message').attr('disabled', 'disabled'); 239 | $('#send').attr('disabled', 'disabled'); 240 | }; 241 | 242 | (function() { 243 | if(ws.readyState === 1) { 244 | ws.send("ping"); 245 | } 246 | if(ws.readyState !== 3) { 247 | setTimeout(arguments.callee, 1000); 248 | } 249 | })(); 250 | 251 | return false; 252 | }); 253 | $('#disconnect').on('click', function() { 254 | ws.close(); 255 | return false; 256 | }); 257 | }); 258 | -------------------------------------------------------------------------------- /src/d3cap/cli.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | use std::collections::hash_map::{Entry, HashMap}; 3 | use std::hash::{Hash}; 4 | use std::fmt::{Display}; 5 | use std::thread::{self, JoinHandle}; 6 | use std::io::{self}; 7 | 8 | use d3cap::{D3capController, ProtocolHandler, PhysDataController}; 9 | use ether::{MacAddr}; 10 | use ip::{AsStdIpAddr}; 11 | 12 | use readline::readline; 13 | 14 | trait TransAddr { 15 | fn trans(&mut self, addr: &T) -> String; 16 | } 17 | 18 | impl TransAddr for HashMap { 19 | fn trans(&mut self, addr: &MacAddr) -> String { 20 | match self.get(addr) { 21 | Some(v) => v.clone(), 22 | None => addr.to_string() 23 | } 24 | } 25 | } 26 | 27 | impl TransAddr for HashMap { 28 | fn trans(&mut self, addr: &T) -> String { 29 | let k = addr.clone(); 30 | match self.entry(k) { 31 | Entry::Occupied(e) => e.get().clone(), 32 | Entry::Vacant(e) => { 33 | let _a = addr.as_std_ip(); 34 | let n = addr.to_string(); 35 | // let n = match net::lookup_addr(&a) { 36 | // Ok(name) => name, 37 | // _ => addr.to_string() 38 | // }; 39 | let out = n.clone(); 40 | e.insert(n); 41 | out 42 | } 43 | } 44 | } 45 | } 46 | 47 | #[derive(Debug)] 48 | enum CliErr { 49 | IoError(io::Error) 50 | } 51 | 52 | impl From for CliErr { 53 | fn from(e: io::Error) -> CliErr { 54 | CliErr::IoError(e) 55 | } 56 | } 57 | 58 | type CliFn = (&'static str, Box, &mut D3capController)->Result<(), CliErr>>); 59 | 60 | pub fn start_cli(ctrl: D3capController) -> io::Result> { 61 | thread::Builder::new().name("cli".to_owned()).spawn(move || { 62 | fn print_ls_addr(ph: &ProtocolHandler, t: &mut T) 63 | where A: Eq+Hash+Copy+Clone+Display+Send+Sync, 64 | T: TransAddr 65 | { 66 | let graph = ph.graph.read().unwrap(); 67 | let mut list: Vec<_> = graph.iter() 68 | .flat_map(|(src_addr, astats)| { 69 | iter::repeat(src_addr).zip(astats.sent_iter()) 70 | }).collect(); 71 | 72 | list.sort_by(|a,b| (a.1).1.count.cmp(&(b.1).1.count).reverse()); 73 | 74 | for &(src_addr, (dst_addr, pstats)) in &list { 75 | println!("{} -> {}: count: {}, size: {}", 76 | t.trans(src_addr), t.trans(dst_addr), pstats.count, pstats.size); 77 | } 78 | } 79 | 80 | fn print_ls_tap>(pd_ctrl: &PhysDataController, macs: &mut T) { 81 | let m = pd_ctrl.map.read().unwrap(); 82 | let mut list: Vec<_> = m.iter() 83 | .filter(|&(_, v)| v.dat.len() > 1).collect(); 84 | 85 | list.sort_by(|a, b| a.1.avg_dist().partial_cmp(&b.1.avg_dist()).unwrap()); 86 | 87 | for i in &list { 88 | let (k, v) = *i; 89 | println!("{:?} [{}, {}, {}]: total: {}, curr_len: {}, dist: {}", 90 | k.0, 91 | macs.trans(&k.1[0]), macs.trans(&k.1[1]), macs.trans(&k.1[2]), 92 | v.count, v.dat.len(), v.avg_dist()); 93 | } 94 | println!(); 95 | } 96 | 97 | let mut ctrl = ctrl; 98 | 99 | let mut cmds: HashMap = HashMap::new(); 100 | 101 | cmds.insert("ping".to_owned(), 102 | ("ping", Box::new(|_, _| { println!("pong"); Ok(()) }))); 103 | 104 | cmds.insert("websocket".to_owned(), 105 | ("websocket", Box::new(|cmd, ctrl| { 106 | match cmd[..] { 107 | [_, port] => { 108 | if let Ok(p) = port.parse() { 109 | ctrl.start_websocket(p)?; 110 | } 111 | }, 112 | [_] => { 113 | ctrl.start_websocket(7432u16)?; 114 | } 115 | _ => println!("Unknown argument") 116 | } 117 | Ok(()) 118 | }))); 119 | 120 | cmds.insert("ls".to_owned(), 121 | ("ls", Box::new(|cmd, ctrl| { 122 | match cmd[1..] { 123 | ["mac"] => print_ls_addr(&ctrl.pg_ctrl.mac, &mut ctrl.mac_names), 124 | ["ip4"] => print_ls_addr(&ctrl.pg_ctrl.ip4, &mut ctrl.ip4_names), 125 | ["ip6"] => print_ls_addr(&ctrl.pg_ctrl.ip6, &mut ctrl.ip6_names), 126 | ["tap"] => print_ls_tap(&ctrl.pd_ctrl, &mut ctrl.mac_names), 127 | _ => println!("Illegal argument") 128 | } 129 | Ok(()) 130 | }))); 131 | 132 | let maxlen = cmds.keys().map(|x| x.len()).max().unwrap(); 133 | 134 | loop { 135 | let val = readline("> ").unwrap(); 136 | let full_cmd: Vec<_> = val.split(' ').collect(); 137 | match full_cmd[0] { 138 | "h" | "help" => { 139 | println!("\nAvailable commands are:"); 140 | for (cmd, &(desc, _)) in &cmds { 141 | println!(" {:2$}\t{}", cmd, desc, maxlen); 142 | } 143 | println!(); 144 | }, 145 | "q" | "quit" | "exit" => break, 146 | "" => {} 147 | cmd => match cmds.get_mut(cmd) { 148 | Some(&mut (_, ref mut f)) => f(full_cmd, &mut ctrl).unwrap(), 149 | None => println!("unknown command") 150 | } 151 | } 152 | } 153 | }) 154 | } 155 | -------------------------------------------------------------------------------- /src/d3cap/d3cap.rs: -------------------------------------------------------------------------------- 1 | use std::thread::{self, JoinHandle}; 2 | use std::hash::{Hash}; 3 | use std::collections::hash_map::{Entry, HashMap}; 4 | use std::fs::File; 5 | use std::io::{self, Read}; 6 | use std::sync::{Arc,RwLock}; 7 | use std::sync::mpsc::{channel, Sender, SendError}; 8 | 9 | use toml; 10 | 11 | use multicast::Multicast; 12 | use json_serve::uiserver::UIServer; 13 | 14 | use util::{ntohs, skip_bytes_cast, skip_cast}; 15 | use ip::{IP4Addr, IP6Addr, IP4Header, IP6Header}; 16 | use ether::{EthernetHeader, MacAddr, 17 | ETHERTYPE_ARP, ETHERTYPE_IP4, ETHERTYPE_IP6, ETHERTYPE_802_1X}; 18 | use dot11::{self, FrameType}; 19 | use tap; 20 | use pkt_graph::{PktMeta, ProtocolGraph, RouteStats}; 21 | use fixed_ring::FixedRingBuffer; 22 | use pcap::pcap as cap; 23 | 24 | 25 | #[derive(RustcEncodable, Clone)] 26 | struct RouteStatsMsg { 27 | typ: &'static str, 28 | route: RouteStats, 29 | } 30 | 31 | #[derive(Debug)] 32 | pub enum Pkt { 33 | Mac(PktMeta), 34 | IP4(PktMeta), 35 | IP6(PktMeta), 36 | } 37 | 38 | #[derive(Clone)] 39 | pub struct ProtocolHandler { 40 | pub typ: &'static str, 41 | pub graph: Arc>>, 42 | stats_mcast: Multicast>, 43 | } 44 | 45 | impl ProtocolHandler { 46 | fn new(typ: &'static str) -> io::Result> { 47 | Ok(ProtocolHandler { 48 | typ: typ, 49 | graph: Arc::new(RwLock::new(ProtocolGraph::new())), 50 | stats_mcast: Multicast::spawn()? 51 | }) 52 | } 53 | 54 | fn update(&mut self, pkt: &PktMeta) { 55 | let route_stats = { 56 | self.graph.write().unwrap().update(pkt) 57 | }; 58 | let route_stats_msg = Arc::new(RouteStatsMsg { 59 | typ: self.typ, 60 | route: route_stats 61 | }); 62 | self.stats_mcast.send(route_stats_msg).unwrap(); 63 | } 64 | } 65 | 66 | #[derive(Clone)] 67 | pub struct ProtoGraphController { 68 | pub cap_tx: Sender, 69 | pub mac: ProtocolHandler, 70 | pub ip4: ProtocolHandler, 71 | pub ip6: ProtocolHandler, 72 | } 73 | 74 | impl ProtoGraphController { 75 | fn spawn() -> io::Result { 76 | let (cap_tx, cap_rx) = channel(); 77 | let ctl = ProtoGraphController { 78 | cap_tx: cap_tx, 79 | mac: ProtocolHandler::new("mac")?, 80 | ip4: ProtocolHandler::new("ip4")?, 81 | ip6: ProtocolHandler::new("ip6")?, 82 | }; 83 | 84 | let mut phctl = ctl.clone(); 85 | thread::Builder::new().name("protocol_handler".to_owned()).spawn(move || { 86 | loop { 87 | let pkt = cap_rx.recv(); 88 | if pkt.is_err() { 89 | break 90 | } 91 | match pkt.unwrap() { 92 | Pkt::Mac(ref p) => phctl.mac.update(p), 93 | Pkt::IP4(ref p) => phctl.ip4.update(p), 94 | Pkt::IP6(ref p) => phctl.ip6.update(p), 95 | } 96 | } 97 | })?; 98 | 99 | Ok(ctl) 100 | } 101 | 102 | fn sender(&self) -> Sender { 103 | self.cap_tx.clone() 104 | } 105 | 106 | fn register_mac_listener(&self, s: Sender>>) { 107 | self.mac.stats_mcast.register(s).unwrap(); 108 | } 109 | 110 | fn register_ip4_listener(&self, s: Sender>>) { 111 | self.ip4.stats_mcast.register(s).unwrap(); 112 | } 113 | 114 | fn register_ip6_listener(&self, s: Sender>>) { 115 | self.ip6.stats_mcast.register(s).unwrap(); 116 | } 117 | } 118 | 119 | enum ParseErr { 120 | Send, 121 | UnknownPacket 122 | } 123 | 124 | impl From> for ParseErr { 125 | fn from(_: SendError) -> ParseErr { 126 | ParseErr::Send 127 | } 128 | } 129 | 130 | trait PktParser { 131 | fn parse(&mut self, pkt: &cap::PcapData) -> Result<(), ParseErr>; 132 | } 133 | 134 | pub struct CaptureCtx { 135 | sess: cap::PcapSession, 136 | parser: Box 137 | } 138 | 139 | impl CaptureCtx { 140 | fn parse_next(&mut self) { 141 | let p = &mut self.parser; 142 | self.sess.next(|cap| { 143 | match p.parse(cap) { 144 | _ => () //just ignore 145 | } 146 | }); 147 | } 148 | } 149 | 150 | struct EthernetParser { 151 | pkts: Sender, 152 | } 153 | 154 | impl PktParser for EthernetParser { 155 | 156 | fn parse(&mut self, pkt: &cap::PcapData) -> Result<(), ParseErr> { 157 | let ether_hdr = unsafe { &*(pkt.pkt_ptr() as *const EthernetHeader) }; 158 | self.pkts.send(Pkt::Mac(PktMeta::new(ether_hdr.src, ether_hdr.dst, pkt.len())))?; 159 | match ether_hdr.typ { 160 | ETHERTYPE_ARP => { 161 | //io::println("ARP!"); 162 | }, 163 | ETHERTYPE_IP4 => { 164 | let ipp: &IP4Header = unsafe { skip_cast(ether_hdr) }; 165 | self.pkts.send(Pkt::IP4(PktMeta::new(ipp.src, ipp.dst, u32::from(ntohs(ipp.len)))))?; 166 | }, 167 | ETHERTYPE_IP6 => { 168 | let ipp: &IP6Header = unsafe { skip_cast(ether_hdr) }; 169 | self.pkts.send(Pkt::IP6(PktMeta::new(ipp.src, ipp.dst, u32::from(ntohs(ipp.len)))))?; 170 | }, 171 | ETHERTYPE_802_1X => { 172 | //io::println("802.1X!"); 173 | }, 174 | _ => { 175 | //println!("Unknown type: {:x}", x); 176 | } 177 | } 178 | Ok(()) 179 | } 180 | } 181 | 182 | #[derive(Debug)] 183 | pub struct PhysData { // TODO: this name sucks 184 | frame_ty: FrameType, 185 | addrs: [MacAddr; 3], 186 | rate: Option, 187 | channel: tap::Channel, 188 | antenna_signal: tap::AntennaSignal, 189 | antenna_noise: tap::AntennaNoise, 190 | antenna: tap::Antenna, 191 | } 192 | 193 | impl PhysData { 194 | fn new(frame_ty: FrameType, 195 | addrs: [MacAddr; 3], 196 | rate: Option, 197 | channel: tap::Channel, 198 | antenna_signal: tap::AntennaSignal, 199 | antenna_noise: tap::AntennaNoise, 200 | antenna: tap::Antenna, 201 | ) -> PhysData { 202 | PhysData { 203 | frame_ty: frame_ty, 204 | addrs: addrs, 205 | rate: rate, 206 | channel: channel, 207 | antenna_signal: antenna_signal, 208 | antenna_noise: antenna_noise, 209 | antenna: antenna 210 | } 211 | } 212 | 213 | fn dist(&self) -> f32 { 214 | let freq = f32::from(self.channel.mhz); 215 | let signal = f32::from(self.antenna_signal.dbm); 216 | 217 | let exp = (27.55 - (20.0 * freq.log10()) + signal.abs()) / 20.0; 218 | (10.0f32).powf(exp) 219 | } 220 | } 221 | 222 | #[derive(PartialEq, Eq, Hash)] 223 | pub struct PhysDataKey(pub FrameType, pub [MacAddr;3]); 224 | 225 | pub struct PhysDataVal { 226 | pub dat: FixedRingBuffer, 227 | pub count: u32, 228 | } 229 | 230 | impl PhysDataVal { 231 | pub fn new() -> PhysDataVal { 232 | PhysDataVal { 233 | dat: FixedRingBuffer::new(10), 234 | count: 0 235 | } 236 | } 237 | 238 | pub fn avg_dist(&self) -> f32 { 239 | let mut s = 0.0; 240 | for pd in self.dat.iter() { 241 | s += pd.dist(); 242 | } 243 | s / (self.dat.len() as f32) 244 | } 245 | 246 | } 247 | 248 | #[derive(Clone)] 249 | pub struct PhysDataController { 250 | pub map: Arc>>, 251 | pd_tx: Sender 252 | } 253 | 254 | impl PhysDataController { 255 | fn spawn() -> io::Result { 256 | let (pd_tx, pd_rx) = channel(); 257 | let out = PhysDataController { 258 | pd_tx: pd_tx, 259 | map: Arc::new(RwLock::new(HashMap::new())) 260 | }; 261 | 262 | let ctl = out.clone(); 263 | thread::Builder::new().name("physdata_handler".to_owned()).spawn(move || { 264 | loop { 265 | let res = pd_rx.recv(); 266 | if res.is_err() { 267 | break 268 | } 269 | let pd = res.unwrap(); 270 | 271 | match ctl.map.write().unwrap().entry(PhysDataKey(pd.frame_ty, pd.addrs)) { 272 | Entry::Occupied(mut e) => { 273 | let mut pdc = e.get_mut(); 274 | pdc.dat.push(pd); 275 | pdc.count += 1; 276 | } 277 | Entry::Vacant(e) => { 278 | let mut pdc = PhysDataVal::new(); 279 | pdc.dat.push(pd); 280 | pdc.count += 1; 281 | e.insert(pdc); 282 | } 283 | }; 284 | } 285 | })?; 286 | 287 | Ok(out) 288 | } 289 | 290 | fn sender(&self) -> Sender { 291 | self.pd_tx.clone() 292 | } 293 | } 294 | 295 | struct RadiotapParser { 296 | pkts: Sender, 297 | phys: Sender 298 | } 299 | 300 | impl RadiotapParser { 301 | fn parse_known_headers(&self, 302 | frame_ty: FrameType, 303 | addrs: [MacAddr; 3], 304 | tap_hdr: &tap::RadiotapHeader) { 305 | match tap_hdr.it_present { 306 | tap::ItPresent::COMMON_A => { 307 | if let Some(vals) = tap::CommonA::parse(tap_hdr) { 308 | self.phys.send(PhysData::new( 309 | frame_ty, 310 | addrs, 311 | Some(vals.rate), 312 | vals.channel, 313 | vals.antenna_signal, 314 | vals.antenna_noise, 315 | vals.antenna 316 | )).unwrap(); 317 | } 318 | }, 319 | tap::ItPresent::COMMON_B => { 320 | if let Some(vals) = tap::CommonB::parse(tap_hdr) { 321 | self.phys.send(PhysData::new( 322 | frame_ty, 323 | addrs, 324 | None, 325 | vals.channel, 326 | vals.antenna_signal, 327 | vals.antenna_noise, 328 | vals.antenna 329 | )).unwrap(); 330 | } 331 | }, 332 | _ => {} //Unknown header 333 | } 334 | } 335 | } 336 | 337 | 338 | impl PktParser for RadiotapParser { 339 | fn parse(&mut self, pkt: &cap::PcapData) -> Result<(), ParseErr> { 340 | fn magic(pkt: &tap::RadiotapHeader) -> &U { 341 | unsafe { skip_bytes_cast(pkt, pkt.it_len as isize) } 342 | } 343 | 344 | let tap_hdr = unsafe { &*(pkt.pkt_ptr() as *const tap::RadiotapHeader) }; 345 | let base: &dot11::Dot11BaseHeader = magic(tap_hdr); 346 | 347 | let fc = &base.fr_ctrl; 348 | if fc.protocol_version() != 0 { 349 | // bogus packet, bail 350 | return Err(ParseErr::UnknownPacket); 351 | } 352 | 353 | match fc.frame_type() { 354 | ft @ FrameType::Management => { 355 | let mgt: &dot11::ManagementFrameHeader = magic(tap_hdr); 356 | self.parse_known_headers(ft, [mgt.addr1, mgt.addr2, mgt.addr3], tap_hdr); 357 | } 358 | ft @ FrameType::Data => { 359 | let data: &dot11::DataFrameHeader = magic(tap_hdr); 360 | //TODO: get length 361 | self.pkts.send(Pkt::Mac(PktMeta::new(data.addr1, data.addr2, 1)))?; 362 | self.parse_known_headers(ft, [data.addr1, data.addr2, data.addr3], tap_hdr); 363 | } 364 | FrameType::Control | FrameType::Unknown => { 365 | //println!("Unknown frame type"); 366 | } 367 | } 368 | Ok(()) 369 | } 370 | 371 | } 372 | 373 | pub fn init_capture(conf: &D3capConf, 374 | pkt_sender: Sender, 375 | pd_sender: Sender) -> CaptureCtx { 376 | let sess = match conf.file { 377 | Some(ref f) => cap::PcapSession::from_file(f), 378 | None => { 379 | println!("No session file"); 380 | let sess_builder = match conf.interface { 381 | Some(ref dev) => cap::PcapSessionBuilder::new_dev(dev), 382 | None => cap::PcapSessionBuilder::new() 383 | }; 384 | 385 | sess_builder.unwrap() 386 | .buffer_size(0xFFFF) 387 | .timeout(1000) 388 | .promisc(conf.promisc) 389 | .rfmon(conf.monitor) 390 | .activate() 391 | } 392 | }; 393 | 394 | let parser = match sess.datalink() { 395 | cap::DLT_ETHERNET => { 396 | Box::new(EthernetParser { pkts: pkt_sender }) as Box 397 | } 398 | cap::DLT_IEEE802_11_RADIO => { 399 | Box::new(RadiotapParser { pkts: pkt_sender, phys: pd_sender }) as Box 400 | } 401 | x => panic!("unsupported datalink type: {}", x) 402 | }; 403 | 404 | CaptureCtx { sess: sess, parser: parser } 405 | } 406 | 407 | pub fn start_capture(conf: D3capConf, 408 | pkt_sender: Sender, 409 | pd_sender: Sender) -> io::Result> { 410 | thread::Builder::new().name("packet_capture".to_owned()).spawn(move || { 411 | let mut cap = init_capture(&conf, pkt_sender, pd_sender); 412 | loop { 413 | cap.parse_next(); 414 | } 415 | }) 416 | } 417 | 418 | enum LoadMacError { 419 | IOError(io::Error), 420 | TomlError(Option) 421 | } 422 | impl From for LoadMacError { 423 | fn from(err: io::Error) -> LoadMacError { 424 | LoadMacError::IOError(err) 425 | } 426 | } 427 | impl From for LoadMacError { 428 | fn from(err: toml::de::Error) -> LoadMacError { LoadMacError::TomlError(Some(err)) } 429 | } 430 | 431 | fn load_mac_addrs(file: &str) -> Result, LoadMacError> { 432 | let mut s = String::new(); 433 | 434 | let mut f = File::open(&file)?; 435 | f.read_to_string(&mut s)?; 436 | 437 | let t = s.parse::()?; 438 | if let Some(k) = t.get(&"known-macs".to_owned()) { 439 | if let Some(tbl) = k.as_table() { 440 | return Ok(tbl.iter() 441 | .map(|(k, v)| (MacAddr::from_string(k), v.as_str())) 442 | .filter_map(|x| match x { 443 | (Some(addr), Some(alias)) => Some((addr, alias.to_owned())), 444 | _ => None 445 | }) 446 | .collect()) 447 | } 448 | } 449 | Err(LoadMacError::TomlError(None)) 450 | } 451 | 452 | fn start_websocket(port: u16, mac_map: &MacMap, pg_ctl: &ProtoGraphController) -> io::Result<()> { 453 | let ui = UIServer::spawn(port, mac_map)?; 454 | pg_ctl.register_mac_listener(ui.create_sender()?); 455 | pg_ctl.register_ip4_listener(ui.create_sender()?); 456 | pg_ctl.register_ip6_listener(ui.create_sender()?); 457 | Ok(()) 458 | } 459 | 460 | pub type MacMap = HashMap; 461 | pub type IP4Map = HashMap; 462 | pub type IP6Map = HashMap; 463 | 464 | #[derive(Clone)] 465 | pub struct D3capController { 466 | pub pg_ctrl: ProtoGraphController, 467 | pub pd_ctrl: PhysDataController, 468 | pub mac_names: MacMap, 469 | pub ip4_names: IP4Map, 470 | pub ip6_names: IP6Map, 471 | pub server_started: bool 472 | } 473 | 474 | impl D3capController { 475 | pub fn spawn(conf: D3capConf) -> io::Result { 476 | let mac_names = conf.conf.as_ref() 477 | .map_or_else(HashMap::new, |x| { 478 | load_mac_addrs(x).unwrap_or_else(|_| HashMap::new()) 479 | }); 480 | let ip4_names = HashMap::new(); 481 | let ip6_names = HashMap::new(); 482 | 483 | let pg_ctrl = ProtoGraphController::spawn()?; 484 | let pd_ctrl = PhysDataController::spawn()?; 485 | 486 | start_capture(conf, pg_ctrl.sender(), pd_ctrl.sender()).unwrap(); 487 | 488 | Ok(D3capController { 489 | pg_ctrl: pg_ctrl, 490 | pd_ctrl: pd_ctrl, 491 | mac_names: mac_names, 492 | ip4_names: ip4_names, 493 | ip6_names: ip6_names, 494 | server_started: false 495 | }) 496 | } 497 | 498 | pub fn start_websocket(&mut self, port: u16) -> io::Result<()> { 499 | if self.server_started { 500 | println!("server already started"); 501 | } else { 502 | start_websocket(port, &self.mac_names, &self.pg_ctrl)?; 503 | self.server_started = true; 504 | } 505 | Ok(()) 506 | } 507 | } 508 | 509 | #[derive(Clone, Debug)] 510 | pub struct D3capConf { 511 | pub websocket: Option, 512 | pub interface: Option, 513 | pub file: Option, 514 | pub conf: Option, 515 | pub promisc: bool, 516 | pub monitor: bool 517 | } 518 | -------------------------------------------------------------------------------- /src/d3cap/dot11.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use ether::{MacAddr}; 4 | 5 | // For possible reference: 6 | // https://github.com/simsong/tcpflow/blob/master/src/wifipcap/wifipcap.h 7 | // For definitive reference: 8 | // http://standards.ieee.org/getieee802/download/802.11-2012.pdf 9 | 10 | bitflags! { 11 | pub struct FrameControlFlags: u8 { 12 | const TO_DS = 1; 13 | const FROM_DS = 1 << 1; 14 | const MORE_FRAGS = 1 << 2; 15 | const RETRY = 1 << 3; 16 | const POWER_MGMT = 1 << 4; 17 | const MORE_DATA = 1 << 5; 18 | const PROTECTED_FRAME = 1 << 6; 19 | const ORDER = 1 << 7; 20 | } 21 | } 22 | 23 | #[derive(Copy, Clone, Debug)] 24 | #[repr(packed)] 25 | pub struct FrameControl { 26 | pub ty: u8, 27 | pub flags: FrameControlFlags 28 | } 29 | 30 | impl FrameControl { 31 | /// When this is non-zero, the packet is bogus; however, being 0 is not sufficient 32 | /// to imply that the packet is good. From verifying with Wireshark and reading 33 | /// around, bogus packets can be pretty common (and are on my network and card), so 34 | /// you need to be able to handle them. 35 | pub fn protocol_version(&self) -> u8 { 36 | self.ty & 0b0000_0011 37 | } 38 | pub fn frame_type(&self) -> FrameType { 39 | match (self.ty & 0b0000_1100) >> 2 { 40 | 0 => FrameType::Management, 41 | 1 => FrameType::Control, 42 | 2 => FrameType::Data, 43 | _ => FrameType::Unknown 44 | } 45 | } 46 | pub fn frame_subtype(&self) -> u8 { 47 | (self.ty & 0b1111_0000) >> 4 48 | } 49 | pub fn has_flag(&self, flag: FrameControlFlags) -> bool { 50 | self.flags.contains(flag) 51 | } 52 | } 53 | 54 | 55 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 56 | pub enum FrameType { 57 | Management, 58 | Control, 59 | Data, 60 | Unknown 61 | } 62 | 63 | //8.2.4.2 Duration/ID field 64 | #[derive(Copy, Clone, Debug)] 65 | #[repr(packed)] 66 | pub struct DurationID { 67 | dur_id: u16 68 | } 69 | 70 | #[derive(Copy, Clone, Debug)] 71 | #[repr(packed)] 72 | pub struct Dot11BaseHeader { 73 | pub fr_ctrl: FrameControl, 74 | pub dur_id: DurationID, 75 | } 76 | 77 | 78 | pub type FCS = [u8; 4]; 79 | 80 | // 8.2.4.3.5 DA field 81 | // The DA field contains an IEEE MAC individual or group address that 82 | // identifies the MAC entity or entities intended as the final 83 | // recipient(s) of the MSDU (or fragment thereof) or A-MSDU, as 84 | // defined in 8.3.2.1, contained in the frame body field. 85 | 86 | // 8.2.4.3.6 SA field 87 | // The SA field contains an IEEE MAC individual address that 88 | // identifies the MAC entity from which the transfer of the MSDU 89 | // (or fragment thereof) or A-MSDU, as defined in 8.3.2.1, contained 90 | // in the frame body field was initiated. The individual/group bit 91 | // is always transmitted as a 0 in the source address. 92 | 93 | // 8.2.4.3.7 RA field 94 | // The RA field contains an IEEE MAC individual or group address that 95 | // identifies the intended immediate recipient STA(s), on the WM, for 96 | // the information contained in the frame body field. 97 | 98 | // 8.2.4.3.8 TA field 99 | // The TA field contains an IEEE MAC individual address that 100 | // identifies the STA that has transmitted, onto the WM, the MPDU 101 | // contained in the frame body field. The Individual/Group bit is 102 | // always transmitted as a 0 in the transmitter address. 103 | 104 | // Frame types 105 | 106 | // 8.3.1 Control Frames 107 | 108 | // 8.3.1.2 RTS 109 | #[repr(packed)] 110 | pub struct RTS { 111 | pub base: Dot11BaseHeader, 112 | pub ra: MacAddr, 113 | pub ta: MacAddr, 114 | pub fcs: FCS 115 | } 116 | 117 | // 8.3.1.3 CTS 118 | #[repr(packed)] 119 | pub struct CTS { 120 | pub base: Dot11BaseHeader, 121 | pub ra: MacAddr, 122 | pub fcs: FCS 123 | } 124 | 125 | // 8.3.1.4 ACK 126 | #[repr(packed)] 127 | pub struct ACK { 128 | pub base: Dot11BaseHeader, 129 | pub ra: MacAddr, 130 | pub fcs: FCS 131 | } 132 | 133 | // 8.3.1.5 PS-Poll 134 | #[repr(packed)] 135 | pub struct PSPoll { 136 | pub base: Dot11BaseHeader, 137 | pub bssid: MacAddr, //ra 138 | pub ta: MacAddr, 139 | pub fcs: FCS 140 | } 141 | 142 | // 8.3.1.6 CF-End 143 | #[repr(packed)] 144 | pub struct CFEnd { 145 | pub base: Dot11BaseHeader, 146 | pub ra: MacAddr, 147 | pub bssid: MacAddr, //ta 148 | pub fcs: FCS 149 | } 150 | 151 | // 8.3.1.7 CF-End+CF-Ack 152 | #[repr(packed)] 153 | pub struct CFEndCFAck { 154 | pub base: Dot11BaseHeader, 155 | pub ra: MacAddr, 156 | pub bssid: MacAddr, //ta 157 | pub fcs: FCS 158 | } 159 | 160 | // 8.3.1.8 BlockAckReq 161 | #[repr(packed)] 162 | pub struct BlockAckReq { 163 | pub base: Dot11BaseHeader, 164 | pub ra: MacAddr, 165 | pub ta: MacAddr, 166 | pub bar_ctl: [u8; 2] 167 | } 168 | 169 | // 8.3.1.9 BlockAck 170 | #[repr(packed)] 171 | pub struct BlockAck { 172 | pub base: Dot11BaseHeader, 173 | pub ra: MacAddr, 174 | pub ta: MacAddr, 175 | pub ba_ctl: [u8; 2] 176 | } 177 | 178 | // 8.3.1.10 Control Wrapper 179 | #[repr(packed)] 180 | pub struct ControlWrapper { 181 | pub base: Dot11BaseHeader, 182 | pub ra: MacAddr, 183 | pub cf_ctl: [u8; 2], 184 | pub ht_ctl: [u8; 4] 185 | } 186 | 187 | // 8.3.2 Data Frames 188 | 189 | // 8.3.2.1 Data Frame Header 190 | #[repr(packed)] 191 | pub struct DataFrameHeader { 192 | pub base: Dot11BaseHeader, 193 | pub addr1: MacAddr, 194 | pub addr2: MacAddr, 195 | pub addr3: MacAddr, 196 | pub seq_ctl: [u8; 2] 197 | } 198 | 199 | // | To DS | From DS | Address 1 | Address 2 | Address 3 | Address 4 | 200 | // | | | | | MSDU | A-MSDU | MSDU | A-MSDU | 201 | // | 0 | 0 | RA = DA | TA = SA | BSSID | BSSID | N/A | N/A | 202 | // | 0 | 1 | RA = DA | TA = BSSID | SA | BSSID | N/A | N/A | 203 | // | 1 | 0 | RA = BSSID | TA = SA | DA | BSSID | N/A | N/A | 204 | // | 1 | 1 | RA | TA | DA | BSSID | SA | BSSID | 205 | 206 | impl DataFrameHeader { 207 | fn get_src(&self) -> MacAddr { 208 | match (self.base.fr_ctrl.has_flag(FrameControlFlags::TO_DS), self.base.fr_ctrl.has_flag(FrameControlFlags::FROM_DS)) { 209 | (_, false) => self.addr2, 210 | (false, true) => self.addr3, 211 | (true, true) => panic!("can't handle this yet") 212 | } 213 | } 214 | 215 | fn get_dest(&self) -> MacAddr { 216 | match (self.base.fr_ctrl.has_flag(FrameControlFlags::TO_DS), self.base.fr_ctrl.has_flag(FrameControlFlags::FROM_DS)) { 217 | (false, _) => self.addr1, 218 | (true, _) => self.addr3, 219 | } 220 | } 221 | } 222 | 223 | // 8.3.3 Management Frames 224 | 225 | // 8.3.3.1 Management Frame Format 226 | #[repr(packed)] 227 | pub struct ManagementFrameHeader { 228 | pub base: Dot11BaseHeader, 229 | pub addr1: MacAddr, 230 | pub addr2: MacAddr, 231 | pub addr3: MacAddr, 232 | pub seq_ctl: [u8; 2], 233 | pub ht_ctl: [u8; 4] 234 | } 235 | -------------------------------------------------------------------------------- /src/d3cap/ether.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Error, Formatter}; 2 | 3 | use rustc_serialize::hex::FromHex; 4 | use rustc_serialize::{Encoder,Encodable}; 5 | 6 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] 7 | pub struct MacAddr([u8; 6]); 8 | 9 | impl Display for MacAddr { 10 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 11 | let &MacAddr(a) = self; 12 | f.write_str(&format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", 13 | a[0], a[1], a[2], a[3], a[4], a[5])) 14 | } 15 | } 16 | 17 | impl Encodable for MacAddr { 18 | fn encode (&self, s: &mut S) -> Result<(), S::Error> { 19 | s.emit_str(&self.to_string()) 20 | } 21 | } 22 | 23 | impl MacAddr { 24 | pub fn from_string(mac: &str) -> Option { 25 | let v: Vec<_> = mac.split(':').collect(); 26 | if v.len() == 6 { 27 | let mut out = [0; 6]; 28 | for (i, s) in v.iter().enumerate() { 29 | match s.from_hex() { 30 | Ok(ref hx) if hx.len() == 1 => out[i] = hx[0], 31 | _ => return None 32 | } 33 | } 34 | Some(MacAddr(out)) 35 | } else { 36 | None 37 | } 38 | } 39 | } 40 | 41 | #[derive(Copy, Clone, Debug)] 42 | #[repr(packed)] 43 | pub struct EthernetHeader { 44 | pub dst: MacAddr, 45 | pub src: MacAddr, 46 | pub typ: u16 47 | } 48 | 49 | //in big-endian order to match packet 50 | pub const ETHERTYPE_ARP: u16 = 0x0608; 51 | pub const ETHERTYPE_IP4: u16 = 0x0008; 52 | pub const ETHERTYPE_IP6: u16 = 0xDD86; 53 | pub const ETHERTYPE_802_1X: u16 = 0x8E88; 54 | -------------------------------------------------------------------------------- /src/d3cap/ip.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display,Error,Formatter}; 2 | use std::{net}; 3 | 4 | use rustc_serialize::{Encodable, Encoder}; 5 | 6 | pub trait AsStdIpAddr { 7 | fn as_std_ip(&self) -> net::IpAddr; 8 | } 9 | 10 | 11 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] 12 | pub struct IP4Addr([u8; 4]); 13 | 14 | impl AsStdIpAddr for IP4Addr { 15 | fn as_std_ip(&self) -> net::IpAddr { 16 | let &IP4Addr(a) = self; 17 | net::IpAddr::V4(net::Ipv4Addr::new(a[0], a[1], a[2], a[3])) 18 | } 19 | } 20 | 21 | impl Display for IP4Addr { 22 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 23 | let &IP4Addr(a) = self; 24 | f.write_str(&format!("{}.{}.{}.{}", a[0], a[1], a[2], a[3])) 25 | } 26 | } 27 | 28 | impl Encodable for IP4Addr { 29 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 30 | s.emit_str(&self.to_string()) 31 | } 32 | } 33 | 34 | 35 | #[repr(packed)] 36 | pub struct IP4Header { 37 | pub ver_ihl: u8, 38 | pub dscp_ecn: u8, 39 | pub len: u16, 40 | pub ident: u16, 41 | pub flags_frag: u16, 42 | pub ttl: u8, 43 | pub proto: u8, 44 | pub hchk: u16, 45 | pub src: IP4Addr, 46 | pub dst: IP4Addr, 47 | } 48 | 49 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] 50 | pub struct IP6Addr([u16; 8]); 51 | 52 | impl AsStdIpAddr for IP6Addr { 53 | fn as_std_ip(&self) -> net::IpAddr { 54 | let &IP6Addr(a) = self; 55 | net::IpAddr::V6(net::Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 56 | } 57 | } 58 | 59 | impl Display for IP6Addr { 60 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 61 | let &IP6Addr(a) = self; 62 | match a { 63 | //ip4-compatible 64 | [0,0,0,0,0,0,g,h] => { 65 | f.write_str(&format!("::{}.{}.{}.{}", 66 | (g >> 8) as u8, g as u8, (h >> 8) as u8, h as u8)) 67 | } 68 | 69 | // ip4-mapped address 70 | [0,0,0,0,0,0xFFFF,g,h] => { 71 | f.write_str(&format!("::FFFF:{}.{}.{}.{}", 72 | (g >> 8) as u8, g as u8, (h >> 8) as u8, h as u8)) 73 | } 74 | 75 | [a,b,c,d,e,f_,g,h] => { 76 | f.write_str(&format!("{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}", 77 | a, b, c, d, e, f_, g, h)) 78 | } 79 | } 80 | } 81 | } 82 | 83 | impl Encodable for IP6Addr { 84 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 85 | s.emit_str(&self.to_string()) 86 | } 87 | } 88 | 89 | 90 | #[repr(packed)] 91 | pub struct IP6Header { 92 | pub ver_tc_fl: u32, 93 | pub len: u16, 94 | pub nxthdr: u8, 95 | pub hoplim: u8, 96 | pub src: IP6Addr, 97 | pub dst: IP6Addr 98 | } 99 | -------------------------------------------------------------------------------- /src/d3cap/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, collections, libc, slice_patterns, lookup_addr)] 2 | 3 | #![plugin(clippy)] 4 | 5 | #![allow(if_not_else, similar_names, enum_variant_names, many_single_char_names, match_same_arms)] 6 | 7 | extern crate getopts; 8 | extern crate time; 9 | extern crate libc; 10 | 11 | #[macro_use] 12 | #[no_link] 13 | extern crate bitflags; 14 | 15 | extern crate toml; 16 | extern crate rustc_serialize; 17 | 18 | extern crate pcap; 19 | extern crate multicast; 20 | extern crate fixed_ring; 21 | extern crate json_serve; 22 | 23 | mod util; 24 | mod ip; 25 | mod ether; 26 | mod dot11; 27 | mod tap; 28 | mod pkt_graph; 29 | mod d3cap; 30 | mod readline; 31 | mod cli; 32 | 33 | 34 | fn main() { 35 | 36 | use getopts as go; 37 | use std::{env}; 38 | use d3cap::{D3capConf, D3capController}; 39 | 40 | let interface_opt = "i"; 41 | let file_opt = "f"; 42 | let conf_opt = "c"; 43 | 44 | let promisc_flag = "P"; 45 | let monitor_flag = "M"; 46 | 47 | let websocket_opt = "websocket"; 48 | let websocket_default = "7432"; 49 | 50 | let mut opts = go::Options::new(); 51 | 52 | opts.optflag("h", "help", "Print this help menu") 53 | .optopt(interface_opt, "interface", "Network interface to listen on", "interface") 54 | .optopt(file_opt, "file", "File to load from", "cap_file") 55 | .optopt(conf_opt, "conf", "Configuration file", "conf_file") 56 | .optflag(promisc_flag, "promisc", "Turn on promiscuous mode") 57 | .optflag(monitor_flag, "monitor", "Turn on monitor mode") 58 | .optflagopt("", websocket_opt, "Run websocket ui server on startup", 59 | &format!("port [{}]", websocket_default)); 60 | 61 | let matches = match opts.parse(env::args()) { 62 | Ok(m) => { m } 63 | Err(f) => { panic!("{}", f) } 64 | }; 65 | 66 | if matches.opt_present("h") { 67 | println!("{}", opts.usage(&opts.short_usage("d3cap"))); 68 | return; 69 | } 70 | 71 | let conf = D3capConf { 72 | websocket: matches.opt_default(websocket_opt, "7432").map(|p| { 73 | match p.parse::() { 74 | Ok(v) => v, 75 | _ => panic!("websocket port must be a number") 76 | } 77 | }), 78 | interface: matches.opt_str(interface_opt), 79 | file: matches.opt_str(file_opt), 80 | conf: matches.opt_str(conf_opt), 81 | promisc: matches.opt_present(promisc_flag), 82 | monitor: matches.opt_present(monitor_flag) 83 | }; 84 | 85 | let mut ctrl = D3capController::spawn(conf.clone()).unwrap(); 86 | 87 | // Only start the websocket server if the option is explicitly provided. 88 | if let Some(port) = conf.websocket { 89 | ctrl.start_websocket(port).unwrap(); 90 | } 91 | 92 | cli::start_cli(ctrl).unwrap().join().unwrap(); 93 | } 94 | -------------------------------------------------------------------------------- /src/d3cap/pkt_graph.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::{self, HashMap}; 2 | use std::collections::hash_map::Entry::{Occupied, Vacant}; 3 | use std::hash::{Hash}; 4 | 5 | use time; 6 | 7 | #[derive(Debug)] 8 | pub struct PktMeta { 9 | pub src: T, 10 | pub dst: T, 11 | pub size: u32, 12 | pub tm: time::Timespec 13 | } 14 | impl PktMeta { 15 | pub fn new(src: T, dst: T, size: u32) -> PktMeta { 16 | PktMeta { src: src, dst: dst, size: size, tm: time::get_time() } 17 | } 18 | } 19 | 20 | #[derive(RustcEncodable, Copy, Clone, Debug)] 21 | pub struct PktStats { 22 | pub count: u64, 23 | pub size: u64 24 | } 25 | impl PktStats { 26 | pub fn new() -> PktStats { 27 | PktStats { count: 0, size: 0 } 28 | } 29 | pub fn update(&mut self, size: u32) { 30 | self.count += 1; 31 | self.size += u64::from(size); 32 | } 33 | } 34 | 35 | //TODO: derive Encodable manually 36 | #[derive(Clone, Debug)] 37 | pub struct AddrStats { 38 | sent: PktStats, 39 | sent_to: HashMap, 40 | received: PktStats, 41 | received_from: HashMap 42 | } 43 | impl <'a, T:Hash+Eq+Clone> AddrStats { 44 | pub fn new() -> AddrStats { 45 | AddrStats { sent: PktStats::new(), sent_to: HashMap::new(), 46 | received: PktStats::new(), received_from: HashMap::new() } 47 | } 48 | 49 | pub fn update_sent_to(&mut self, to: T, size: u32) -> PktStats { 50 | self.sent.update(size); 51 | AddrStats::update(&mut self.sent_to, to, size) 52 | } 53 | 54 | pub fn get_sent(&self) -> PktStats { 55 | self.sent 56 | } 57 | 58 | pub fn get_sent_to(&self, to: &T) -> PktStats { 59 | AddrStats::get(&self.sent_to, to) 60 | } 61 | 62 | pub fn sent_iter(&'a self) -> ASIter<'a, T> { 63 | ASIter { inner: self.sent_to.iter() } 64 | } 65 | 66 | 67 | pub fn update_received_from(&mut self, from: T, size: u32) -> PktStats { 68 | self.received.update(size); 69 | AddrStats::update(&mut self.received_from, from, size) 70 | } 71 | 72 | pub fn get_received(&self) -> PktStats { 73 | self.received 74 | } 75 | 76 | pub fn get_received_from(&self, from: &T) -> PktStats { 77 | AddrStats::get(&self.received_from, from) 78 | } 79 | 80 | pub fn recv_iter(&'a self) -> ASIter<'a, T> { 81 | ASIter { inner: self.received_from.iter() } 82 | } 83 | 84 | 85 | 86 | fn get(m: &HashMap, addr: &T) -> PktStats { 87 | match m.get(addr) { 88 | Some(s) => *s, 89 | None => PktStats::new() 90 | } 91 | } 92 | 93 | fn update(m: &mut HashMap, addr: T, size: u32) -> PktStats { 94 | let stats = match m.entry(addr) { 95 | Vacant(entry) => entry.insert(PktStats::new()), 96 | Occupied(entry) => entry.into_mut() 97 | }; 98 | stats.update(size); 99 | *stats 100 | } 101 | } 102 | 103 | pub struct ASIter<'a, T:'a> { 104 | inner: hash_map::Iter<'a, T, PktStats> 105 | } 106 | 107 | impl<'a, T: 'a+Hash+Eq+Copy+Clone> Iterator for ASIter<'a, T> { 108 | type Item = (&'a T, &'a PktStats); 109 | 110 | fn next(&mut self) -> Option<::Item> { 111 | self.inner.next() 112 | } 113 | 114 | fn size_hint(&self) -> (usize, Option) { 115 | self.inner.size_hint() 116 | } 117 | } 118 | 119 | 120 | #[derive(RustcEncodable, Clone)] 121 | pub struct SentStats { 122 | addr: T, 123 | sent: PktStats 124 | } 125 | 126 | #[derive(RustcEncodable, Clone)] 127 | pub struct RouteStats { 128 | a: SentStats, 129 | b: SentStats 130 | } 131 | 132 | //TODO: derive Encodable manually 133 | #[derive(Clone, Debug)] 134 | pub struct ProtocolGraph { 135 | stats: PktStats, 136 | routes: HashMap>, 137 | } 138 | 139 | impl<'a, T: Hash+Eq+Copy+Clone> ProtocolGraph { 140 | pub fn new() -> ProtocolGraph { 141 | ProtocolGraph { stats: PktStats::new(), routes: HashMap::new() } 142 | } 143 | pub fn update(&mut self, pkt: &PktMeta) -> RouteStats { 144 | self.stats.update(pkt.size); 145 | 146 | // TODO: can we do something to avoid all these clones? 147 | let a_to_b; 148 | { 149 | let a = match self.routes.entry(pkt.src) { 150 | Vacant(entry) => entry.insert(AddrStats::new()), 151 | Occupied(entry) => entry.into_mut() 152 | }; 153 | a_to_b = a.update_sent_to(pkt.dst, pkt.size); 154 | } 155 | 156 | let b_to_a; 157 | { 158 | let b = match self.routes.entry(pkt.dst) { 159 | Vacant(entry) => entry.insert(AddrStats::new()), 160 | Occupied(entry) => entry.into_mut() 161 | }; 162 | b.update_received_from(pkt.src, pkt.size); 163 | b_to_a = b.get_sent_to(&pkt.src); 164 | } 165 | 166 | RouteStats { 167 | a: SentStats { addr: pkt.src, sent: a_to_b }, 168 | b: SentStats { addr: pkt.dst, sent: b_to_a } 169 | } 170 | } 171 | 172 | pub fn get_route_stats(&self, a: &T, b: &T) -> Option> { 173 | let a_opt = self.routes.get(a); 174 | let b_opt = self.routes.get(b); 175 | match (a_opt, b_opt) { 176 | (Some(a_), Some(b_)) => Some(RouteStats { 177 | a: SentStats { addr: *a, sent: a_.get_sent_to(b) }, 178 | b: SentStats { addr: *b, sent: b_.get_sent_to(a) } 179 | }), 180 | _ => None 181 | } 182 | } 183 | 184 | pub fn get_addr_stats(&self, addr: &T) -> Option<&AddrStats> { 185 | self.routes.get(addr) 186 | } 187 | 188 | pub fn iter(&'a self) -> PGIter<'a, T> { 189 | PGIter { inner: self.routes.iter() } 190 | } 191 | } 192 | 193 | pub struct PGIter<'a, T:'a+Hash+Eq> { 194 | inner: hash_map::Iter<'a, T, AddrStats> 195 | } 196 | 197 | impl<'a, T: 'a+Hash+Eq+Copy+Clone> Iterator for PGIter<'a, T> { 198 | type Item = (&'a T, &'a AddrStats); 199 | 200 | fn next(&mut self) -> Option<::Item> { 201 | self.inner.next() 202 | } 203 | 204 | fn size_hint(&self) -> (usize, Option) { 205 | self.inner.size_hint() 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/d3cap/readline.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{self, CString, CStr}; 2 | use std::str; 3 | use libc; 4 | 5 | mod raw { 6 | use libc; 7 | 8 | #[link(name="readline")] 9 | extern { 10 | pub fn readline(p: *const libc::c_char) -> *const libc::c_char; 11 | pub fn add_history(p: *const libc::c_char); 12 | } 13 | } 14 | 15 | #[derive(Debug)] 16 | #[allow(enum_variant_names)] 17 | pub enum ReadlineError { 18 | NullInPrompt(ffi::NulError), 19 | NullOnRead 20 | } 21 | 22 | pub fn readline(prompt: &str) -> Result { 23 | let cprompt = match CString::new(prompt.as_bytes()) { 24 | Ok(s) => s, 25 | Err(e) => return Err(ReadlineError::NullInPrompt(e)) 26 | }; 27 | 28 | let in_buf = cprompt.as_ptr(); 29 | unsafe { 30 | let raw = raw::readline(in_buf as *const libc::c_char); 31 | if !raw.is_null() { 32 | let slice = CStr::from_ptr(raw).to_bytes(); 33 | match str::from_utf8(slice).map(|ret| ret.trim()) { 34 | Ok(a) if !a.is_empty() => { 35 | raw::add_history(raw); 36 | Ok(a.to_owned()) 37 | } 38 | _ => Ok("".to_owned()) 39 | } 40 | } else { 41 | Err(ReadlineError::NullOnRead) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/d3cap/tap.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::mem::size_of; 4 | 5 | use util::skip_cast; 6 | 7 | //For possible reference: 8 | //https://github.com/simsong/tcpflow/blob/master/src/wifipcap/ieee802_11_radio.h 9 | 10 | #[derive(Copy,Clone,Debug)] 11 | #[repr(packed)] 12 | pub struct RadiotapHeader { 13 | pub it_version: u8, // 8 -> 1 14 | pub it_pad: u8, // 8 -> 1 15 | pub it_len: u16, // 16 -> 2 16 | pub it_present: ItPresent // 32 -> 4 17 | } 18 | //size_of should == 8 19 | 20 | bitflags! { 21 | pub struct ItPresent: u32 { 22 | const TSFT = 1; 23 | const FLAGS = 1 << 1; 24 | const RATE = 1 << 2; 25 | const CHANNEL = 1 << 3; 26 | const FHSS = 1 << 4; 27 | const ANTENNA_SIGNAL = 1 << 5; 28 | const ANTENNA_NOISE = 1 << 6; 29 | const LOCK_QUALITY = 1 << 7; 30 | const TX_ATTENUATION = 1 << 8; 31 | const DB_TX_ATTENUATION = 1 << 9; 32 | const DBM_TX_POWER = 1 << 10; 33 | const ANTENNA = 1 << 11; 34 | const DB_ANTENNA_SIGNAL = 1 << 12; 35 | const DB_ANTENNA_NOISE = 1 << 13; 36 | const RX_FLAGS = 1 << 14; 37 | const TX_FLAGS = 1 << 15; 38 | const RTS_RETRIES = 1 << 16; 39 | const DATA_RETRIES = 1 << 17; 40 | const MCS = 1 << 19; 41 | const A_MPDU_STATUS = 1 << 20; 42 | const VHT = 1 << 21; 43 | const MORE_IT_PRESENT = 1 << 31; 44 | 45 | const COMMON_A = Self::TSFT.bits 46 | | Self::FLAGS.bits 47 | | Self::RATE.bits 48 | | Self::CHANNEL.bits 49 | | Self::ANTENNA_SIGNAL.bits 50 | | Self::ANTENNA_NOISE.bits 51 | | Self::ANTENNA.bits; 52 | 53 | const COMMON_B = Self::TSFT.bits 54 | | Self::FLAGS.bits 55 | | Self::CHANNEL.bits 56 | | Self::ANTENNA_SIGNAL.bits 57 | | Self::ANTENNA_NOISE.bits 58 | | Self::ANTENNA.bits 59 | | Self::MCS.bits; 60 | } 61 | } 62 | 63 | #[derive(Copy,Clone,Debug)] 64 | #[repr(packed)] 65 | pub struct Tsft { 66 | pub timer_micros: u64 67 | } 68 | 69 | bitflags! { 70 | pub struct Flags: u8 { 71 | const DURING_CFP = 0x01; 72 | const SHORT_PREAMBLE = 0x02; 73 | const ENCRYPT_WEP = 0x04; 74 | const FRAGMENTATION = 0x08; 75 | const INCLUDES_FCS = 0x10; 76 | const HAS_PADDING = 0x20; 77 | const FAILED_FCS_CHK = 0x40; 78 | const SHORT_GUARD = 0x80; 79 | } 80 | } 81 | 82 | #[derive(Copy,Clone,Debug)] 83 | #[repr(packed)] 84 | pub struct Rate { 85 | pub in_500kbps: u8 86 | } 87 | 88 | bitflags! { 89 | pub struct ChannelFlags: u16 { 90 | const TURBO = 0x0010; 91 | const CCK = 0x0020; 92 | const OFDM = 0x0040; 93 | const GHZ_2 = 0x0080; 94 | const GHZ_5 = 0x0100; 95 | const PSV_SCAN = 0x0200; 96 | const DYN_CCK_OFDM = 0x0400; 97 | const GFSK = 0x0800; 98 | } 99 | } 100 | 101 | 102 | #[derive(Copy,Clone,Debug)] 103 | #[repr(packed)] 104 | pub struct Channel { 105 | pub mhz: u16, 106 | pub flags: ChannelFlags 107 | } 108 | 109 | #[derive(Copy,Clone,Debug)] 110 | #[repr(packed)] 111 | pub struct AntennaSignal { 112 | pub dbm: i8 113 | } 114 | 115 | #[derive(Copy,Clone,Debug)] 116 | #[repr(packed)] 117 | pub struct AntennaNoise { 118 | pub dbm: i8 119 | } 120 | 121 | #[derive(Copy,Clone,Debug)] 122 | #[repr(packed)] 123 | pub struct Antenna { 124 | pub idx: u8 125 | } 126 | 127 | #[derive(Copy,Clone,Debug)] 128 | #[repr(packed)] 129 | pub struct Mcs { 130 | pub known: u8, 131 | pub flags: u8, 132 | pub mcs: u8 133 | } 134 | 135 | // For now just predefining a few types of packets I actually see with my setup, 136 | // rather than defining a general parser. 137 | #[derive(Copy,Clone)] 138 | #[repr(packed)] 139 | pub struct CommonA { 140 | pub tsft: Tsft, // 8 141 | pub flags: Flags, // 1 142 | pub rate: Rate, // 1 143 | pub channel: Channel, // 2 + 2 = 4 144 | pub antenna_signal: AntennaSignal, // 1 145 | pub antenna_noise: AntennaNoise, // 1 146 | pub antenna: Antenna // 1 147 | } 148 | // sizeof should be 17. 149 | 150 | impl CommonA { 151 | pub fn parse(hdr: &RadiotapHeader) -> Option<&CommonA> { 152 | let sz = size_of::() + size_of::(); 153 | if hdr.it_present == ItPresent::COMMON_A 154 | && hdr.it_len as usize >= sz { 155 | let out: &CommonA = unsafe { skip_cast(hdr) }; 156 | Some(out) 157 | } else { 158 | None 159 | } 160 | } 161 | } 162 | 163 | #[repr(packed)] 164 | pub struct CommonB { 165 | pub tsft: Tsft, 166 | pub flags: Flags, 167 | pub channel: Channel, 168 | pub antenna_signal: AntennaSignal, 169 | pub antenna_noise: AntennaNoise, 170 | pub antenna: Antenna, 171 | pub mcs: Mcs 172 | } 173 | 174 | impl CommonB { 175 | pub fn parse(hdr: &RadiotapHeader) -> Option<&CommonB> { 176 | let sz = size_of::() + size_of::(); 177 | if hdr.it_present == ItPresent::COMMON_B 178 | && hdr.it_len as usize >= sz { 179 | let out: &CommonB = unsafe { skip_cast(hdr) }; 180 | Some(out) 181 | } else { 182 | None 183 | } 184 | } 185 | } 186 | 187 | 188 | 189 | 190 | impl RadiotapHeader { 191 | pub fn has_field(&self, fld: ItPresent) -> bool { 192 | self.it_present.contains(fld) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/d3cap/util.rs: -------------------------------------------------------------------------------- 1 | 2 | //TODO: this is dumb and just assumes we're on a little-endian system. 3 | pub fn ntohs(n: u16) -> u16 { 4 | (n>>8) | (n<<8) 5 | } 6 | 7 | pub unsafe fn skip_cast(t: &T) -> &U { 8 | &*((t as *const T).offset(1) as *const U) 9 | } 10 | 11 | pub unsafe fn skip_bytes_cast(t: &T, bytes: isize) -> &U { 12 | &*((t as *const T as *const u8).offset(bytes) as *const U) 13 | } 14 | -------------------------------------------------------------------------------- /src/fixed_ring/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "fixed_ring" 4 | version = "0.0.1" 5 | authors = [ "jfager@gmail.com" ] 6 | 7 | [dependencies] 8 | rustc-serialize = "*" 9 | clippy = "*" 10 | -------------------------------------------------------------------------------- /src/fixed_ring/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin)] 2 | #![plugin(clippy)] 3 | 4 | // Example modified from https://github.com/mozilla/rust/issues/3562#issuecomment-9210203 5 | 6 | // Fixed-size ring buffer: when it is at capacity push will drop the oldest element. 7 | 8 | 9 | extern crate rustc_serialize; 10 | 11 | use std::iter::Iterator; 12 | use std::fmt; 13 | use std::fmt::{Debug,Formatter}; 14 | 15 | use rustc_serialize::{Encoder, Encodable}; 16 | 17 | pub struct FixedRingBuffer { 18 | buffer: Vec, 19 | capacity: usize, // number of elements the buffer is able to hold (can't guarantee that vec capacity is exactly what we set it to) 20 | size: usize, // number of elements with legit values in the buffer 21 | next: usize, // index at which new elements land 22 | } 23 | 24 | impl FixedRingBuffer { 25 | pub fn new(capacity: usize) -> FixedRingBuffer { 26 | FixedRingBuffer { 27 | buffer: Vec::with_capacity(capacity), 28 | capacity: capacity, 29 | size: 0, 30 | next: 0 31 | } 32 | } 33 | 34 | pub fn len(&self) -> usize { 35 | self.size 36 | } 37 | 38 | pub fn is_empty(&self) -> bool { 39 | self.size == 0 40 | } 41 | 42 | pub fn clear(&mut self) { 43 | self.buffer.truncate(0); 44 | self.size = 0; 45 | self.next = 0; 46 | } 47 | 48 | pub fn iter(&self) -> RingIterator { 49 | RingIterator { rb: self, i: 0 } 50 | } 51 | 52 | pub fn push(&mut self, element: T) { 53 | assert!(self.capacity > 0); 54 | 55 | if self.size < self.capacity { 56 | self.buffer.push(element); 57 | self.size += 1; 58 | } else { 59 | self.buffer[self.next] = element; 60 | } 61 | self.next = (self.next + 1) % self.capacity; 62 | } 63 | } 64 | 65 | impl Encodable for FixedRingBuffer { 66 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 67 | s.emit_seq(self.len(), |s| { 68 | for (i, e) in self.iter().enumerate() { 69 | s.emit_seq_elt(i, |s| e.encode(s))?; 70 | } 71 | Ok(()) 72 | }) 73 | } 74 | } 75 | 76 | impl std::ops::Index for FixedRingBuffer { 77 | type Output = T; 78 | 79 | fn index(&self, index: usize) -> &T { 80 | assert!(index < self.size); 81 | 82 | if self.size < self.capacity { 83 | &self.buffer[index] 84 | } else { 85 | &self.buffer[(self.next + index) % self.capacity] 86 | } 87 | } 88 | } 89 | 90 | pub struct RingIterator<'s, T:'s> { 91 | rb: &'s FixedRingBuffer, 92 | i: usize 93 | } 94 | 95 | impl<'s, T> Iterator for RingIterator<'s, T> { 96 | type Item = &'s T; 97 | 98 | fn next(&mut self) -> Option<&'s T> { 99 | if self.i < self.rb.size { 100 | let out = Some(&self.rb[self.i]); 101 | self.i += 1; 102 | out 103 | } else { 104 | None 105 | } 106 | } 107 | } 108 | 109 | impl Debug for FixedRingBuffer { 110 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 111 | write!(f, "[")?; 112 | let mut first = true; 113 | for e in self.iter() { 114 | if !first { 115 | write!(f, ", ")?; 116 | } 117 | first = false; 118 | e.fmt(f)?; 119 | } 120 | write!(f, "]") 121 | } 122 | } 123 | 124 | #[test] 125 | fn test_basics() { 126 | // size 0 127 | let buffer: FixedRingBuffer = FixedRingBuffer::new(0); // rust type inference works very well, but not in this case 128 | assert!(buffer.len() == 0); 129 | 130 | // size 1 131 | let mut buffer = FixedRingBuffer::new(1); 132 | assert!(buffer.len() == 0); 133 | 134 | buffer.push(2); 135 | assert!(buffer.len() == 1); 136 | assert!(buffer[0] == 2); 137 | 138 | buffer.push(3); 139 | assert!(buffer.len() == 1); 140 | assert!(buffer[0] == 3); 141 | 142 | // size 4 143 | let mut buffer = FixedRingBuffer::new(4); 144 | assert!(buffer.len() == 0); 145 | 146 | buffer.push(1); 147 | assert!(buffer.len() == 1); 148 | assert!(buffer[0] == 1); 149 | 150 | buffer.push(2); 151 | assert!(buffer.len() == 2); 152 | assert!(buffer[0] == 1); 153 | assert!(buffer[1] == 2); 154 | 155 | buffer.push(3); 156 | assert!(buffer.len() == 3); 157 | assert!(buffer[0] == 1); 158 | assert!(buffer[1] == 2); 159 | assert!(buffer[2] == 3); 160 | 161 | buffer.push(4); 162 | assert!(buffer.len() == 4); 163 | assert!(buffer[0] == 1); 164 | assert!(buffer[1] == 2); 165 | assert!(buffer[2] == 3); 166 | assert!(buffer[3] == 4); 167 | 168 | // At this point the elements have wrapped around. 169 | buffer.push(5); 170 | assert!(buffer.len() == 4); 171 | assert!(buffer[3] == 5); 172 | 173 | // But the public API hides this from clients (and the private fields 174 | // can only be used within this module). 175 | assert!(buffer[0] == 2); 176 | assert!(buffer[1] == 3); 177 | assert!(buffer[2] == 4); 178 | assert!(buffer[3] == 5); 179 | assert!(buffer.to_string() == "[2, 3, 4, 5]".to_string()); 180 | 181 | // clear 182 | buffer.clear(); 183 | assert!(buffer.len() == 0); 184 | 185 | buffer.push(2); 186 | assert!(buffer.len() == 1); 187 | assert!(buffer[0] == 2); 188 | 189 | buffer.push(3); 190 | assert!(buffer.len() == 2); 191 | assert!(buffer[0] == 2); 192 | assert!(buffer[1] == 3); 193 | } 194 | 195 | // Rust uses a lot of functional programming idioms. One that takes some getting 196 | // used to for imperative programmers is an avoidance of loops (loops rely on 197 | // mutation of a loop variable which is not functional style). Instead looping is 198 | // typically done with functions taking closures, the most common of which are: 199 | // each, map, filter, and fold. 200 | #[test] 201 | fn test_functional() { 202 | let mut buffer: FixedRingBuffer = FixedRingBuffer::new(4); 203 | buffer.push(1); 204 | buffer.push(3); 205 | buffer.push(5); 206 | buffer.push(2); 207 | 208 | // each calls a closure with each element 209 | // it is more functional than an explicit loop, but requires side effects in order to 210 | // do anything useful (because the closures user's give to each don't return values) 211 | let mut max = 0; 212 | for element in buffer.iter() { 213 | if *element > max {max = *element} // dereference because each returns elements by reference 214 | } 215 | assert!(max == 5); 216 | 217 | let odd: Vec<_> = buffer.iter().map(|e| {*e & 1 == 1}).collect(); 218 | assert!(odd == vec![true, true, true, false]); 219 | 220 | // filter returns elements for which the closure returns true 221 | let odd: Vec<_> = buffer.iter().filter_map(|&e| { 222 | if e & 1 == 1 { Some(e) } else { None } 223 | }).collect(); 224 | assert!(odd == vec![1, 3, 5]); 225 | 226 | // fold uses the closure to combine elements together (possibly into a different type) 227 | // either forwards (foldl) or in reverse (foldr) 228 | let sum: i32 = buffer.iter().fold(0, |a, &b| a + b); 229 | assert!(sum == 1 + 3 + 5 + 2); 230 | } 231 | -------------------------------------------------------------------------------- /src/json_serve/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "json_serve" 4 | version = "0.0.1" 5 | authors = [ "jfager@gmail.com" ] 6 | 7 | 8 | [dependencies] 9 | rustc-serialize = "*" 10 | byteorder = "*" 11 | openssl = "*" 12 | 13 | [dependencies.multicast] 14 | path = "../multicast" 15 | 16 | [profile.dev] 17 | debug = false 18 | -------------------------------------------------------------------------------- /src/json_serve/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type="lib"] 2 | 3 | extern crate openssl; 4 | extern crate multicast; 5 | extern crate rustc_serialize; 6 | extern crate byteorder; 7 | 8 | pub mod uiserver; 9 | 10 | mod rustwebsocket; 11 | -------------------------------------------------------------------------------- /src/json_serve/src/rustwebsocket.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self,BufRead,Write}; 2 | 3 | use rustc_serialize::base64::{ToBase64, STANDARD}; 4 | use byteorder::{BigEndian, WriteBytesExt}; 5 | 6 | use openssl::sha::sha1; 7 | 8 | const CONNECTION_FIELD: &'static str = "Connection"; 9 | const UPGRADE: &'static str = "upgrade"; 10 | const UPGRADE_FIELD: &'static str = "Upgrade"; 11 | const WEBSOCKET: &'static str = "websocket"; 12 | const HOST_FIELD: &'static str = "Host"; 13 | const ORIGIN_FIELD: &'static str = "Origin"; 14 | const KEY_FIELD: &'static str = "Sec-WebSocket-Key"; 15 | const PROTOCOL_FIELD: &'static str = "Sec-WebSocket-Protocol"; 16 | const VERSION_FIELD: &'static str = "Sec-WebSocket-Version"; 17 | const VERSION: &'static str = "13"; 18 | const ACCEPT_FIELD: &'static str = "Sec-WebSocket-Accept"; 19 | const SECRET: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 20 | 21 | pub enum FrameType { 22 | Empty = 0xF0, 23 | Error = 0xF1, 24 | Incomplete = 0xF2, 25 | Text = 0x01, 26 | Binary = 0x02, 27 | Ping = 0x09, 28 | Pong = 0x0A, 29 | Opening = 0xF3, 30 | Closing = 0x08 31 | } 32 | 33 | fn from_prim(prim: u8) -> Option { 34 | use self::FrameType::*; 35 | match prim { 36 | 0xF0 => Some(Empty), 37 | 0xF1 => Some(Error), 38 | 0xF2 => Some(Incomplete), 39 | 0x01 => Some(Text), 40 | 0x02 => Some(Binary), 41 | 0x09 => Some(Ping), 42 | 0x0A => Some(Pong), 43 | 0xF3 => Some(Opening), 44 | 0x0 => Some(Closing), 45 | _ => None 46 | } 47 | } 48 | 49 | enum State { 50 | Opening, 51 | Normal, 52 | Closing 53 | } 54 | 55 | pub struct Handshake { 56 | key: String, 57 | resource: String, 58 | frame_type: FrameType 59 | } 60 | 61 | impl Handshake { 62 | pub fn get_answer(&self) -> String { 63 | let res = sha1(self.key.as_bytes()); 64 | let response_key = res.to_base64(STANDARD); 65 | format!("HTTP/1.1 101 Switching Protocols\r\n\ 66 | {}: {}\r\n\ 67 | {}: {}\r\n\ 68 | {}: {}\r\n\r\n", 69 | UPGRADE_FIELD, WEBSOCKET, 70 | CONNECTION_FIELD, UPGRADE_FIELD, 71 | ACCEPT_FIELD, response_key) 72 | } 73 | } 74 | 75 | pub fn parse_handshake(r: &mut R) -> Option { 76 | let mut line = String::new(); 77 | if r.read_line(&mut line).is_err() { 78 | return None 79 | }; 80 | 81 | let prop: Vec<_> = line.split(" ").collect(); 82 | let mut hs = Handshake { 83 | //host: ~"", 84 | //origin: ~"", 85 | key: "".to_string(), 86 | resource: prop[1].trim().to_string(), 87 | frame_type: FrameType::Opening 88 | }; 89 | 90 | let mut has_handshake = false; 91 | loop { 92 | let mut line = String::new(); 93 | if r.read_line(&mut line).is_err() { 94 | return if has_handshake { Some(hs) } else { None } 95 | }; 96 | 97 | let line = line.trim(); 98 | if line.is_empty() { 99 | return if has_handshake { Some(hs) } else { None }; 100 | } 101 | 102 | let prop: Vec<_> = line.split(": ").collect(); 103 | if prop.len() != 2 { 104 | println!("Unexpected line: '{}'", line); 105 | return None; 106 | } 107 | let key = prop[0].trim(); 108 | let val = prop[1].trim(); 109 | 110 | match key { 111 | KEY_FIELD => { 112 | hs.key = val.to_string(); 113 | hs.key.push_str(SECRET); 114 | has_handshake = true; 115 | } 116 | _ => () //do nothing 117 | } 118 | } 119 | } 120 | 121 | static SMALL_FRAME: usize = 125; 122 | static MED_FRAME: usize = 65535; 123 | 124 | static MED_FRAME_FLAG: u8 = 126; 125 | static LARGE_FRAME_FLAG: u8 = 127; 126 | 127 | pub fn write_frame(data: &[u8], frame_type: FrameType, w: &mut W) -> io::Result<()> { 128 | w.write_u8((0x80 | frame_type as u32) as u8)?; 129 | 130 | if data.len() <= SMALL_FRAME { 131 | w.write_u8(data.len() as u8)?; 132 | } else if data.len() <= MED_FRAME { 133 | w.write_u8(MED_FRAME_FLAG)?; 134 | w.write_u16::(data.len() as u16)?; 135 | } else { 136 | w.write_u8(LARGE_FRAME_FLAG)?; 137 | w.write_u64::(data.len() as u64)?; 138 | } 139 | w.write_all(data)?; 140 | w.flush() 141 | } 142 | 143 | pub fn parse_input_frame(r: &mut R) -> (Option>, FrameType) { 144 | let mut hdr = [0; 2]; 145 | match r.read(&mut hdr) { 146 | Ok(sz) => if sz != 2 { return (None, FrameType::Error) }, 147 | _ => return (None, FrameType::Error) 148 | }; 149 | 150 | if hdr[0] & 0x70 != 0x0 //extensions must be off 151 | || hdr[0] & 0x80 != 0x80 //no continuation frames 152 | || hdr[1] & 0x80 != 0x80 { //masking bit must be set 153 | return (None, FrameType::Error); 154 | } 155 | 156 | let opcode = hdr[0] & 0x0F; 157 | if let Some(frame_type) = from_prim(opcode) { 158 | let payload_len = hdr[1] & 0x7F; 159 | if payload_len < 0x7E { //Only handle short payloads right now. 160 | let toread = (payload_len + 4) as usize; //+4 for mask 161 | let mut masked_payload = vec![0; toread]; 162 | match r.read(&mut masked_payload) { 163 | Ok(sz) => if sz != toread { return (None, FrameType::Error) }, 164 | _ => return (None, FrameType::Error) 165 | }; 166 | let payload = masked_payload[4..].iter() 167 | .enumerate() 168 | .map(|(i, t)| { *t ^ masked_payload[i%4] }) 169 | .collect(); 170 | return (Some(payload), frame_type); 171 | } 172 | 173 | return (None, frame_type); 174 | } 175 | 176 | return (None, FrameType::Error); 177 | } 178 | -------------------------------------------------------------------------------- /src/json_serve/src/uiserver.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; 2 | use std::io::{self,BufRead,BufReader,Write,BufWriter}; 3 | use std::net::{TcpListener}; 4 | use std::thread; 5 | use std::sync::Arc; 6 | 7 | use rustc_serialize::{json, Encodable}; 8 | 9 | use rustwebsocket as ws; 10 | 11 | use multicast::{Multicast}; 12 | 13 | #[derive(Copy,Clone)] 14 | pub struct WebSocketWorker; 15 | 16 | impl WebSocketWorker { 17 | fn handshake(&self, r: &mut R, w: &mut W) -> io::Result<()> { 18 | match ws::parse_handshake(r) { 19 | Some(hs) => { 20 | w.write_all(hs.get_answer().as_bytes())?; 21 | w.flush()?; 22 | } 23 | None => { 24 | w.write_all("HTTP/1.1 404 Not Found\r\n\r\n".as_bytes())?; 25 | } 26 | } 27 | Ok(()) 28 | } 29 | 30 | fn run(&self, r: &mut R, w: &mut W, data_po: &Receiver>) -> io::Result<()> { 31 | self.handshake(r, w)?; 32 | loop { 33 | let mut counter = 0u32; 34 | loop { 35 | match data_po.try_recv() { 36 | Ok(msg) => { 37 | let res = ws::write_frame(msg.as_bytes(), ws::FrameType::Text, w); 38 | if res.is_err() { 39 | println!("Error writing msg frame: {:?}", res); 40 | break 41 | } 42 | if counter < 100 { 43 | counter += 1; 44 | } else { 45 | break 46 | } 47 | }, 48 | Err(TryRecvError::Empty) => { 49 | break 50 | }, 51 | Err(TryRecvError::Disconnected) => { 52 | panic!("Disconnected from client") 53 | } 54 | } 55 | } 56 | let (_, frame_type) = ws::parse_input_frame(r); 57 | match frame_type { 58 | ws::FrameType::Closing | 59 | ws::FrameType::Error => { 60 | let res = ws::write_frame(&[], ws::FrameType::Closing, w); 61 | if res.is_err() { 62 | println!("Error writing closing frame: {:?}", res); 63 | } 64 | break; 65 | } 66 | _ => () 67 | } 68 | } 69 | Ok(()) 70 | } 71 | } 72 | 73 | pub struct UIServer { 74 | json_multicast: Multicast, //UIServer -> Workers (json msgs) 75 | } 76 | 77 | impl UIServer { 78 | pub fn spawn(port: u16, welcome: &T) -> io::Result { 79 | let welcome_msg = Arc::new(json::encode(welcome).unwrap()); 80 | 81 | let mc = Multicast::spawn()?; 82 | let json_dest_sender = mc.clone(); 83 | 84 | thread::Builder::new().name("ui_server".to_string()).spawn(move || { 85 | let listener = TcpListener::bind(&("127.0.0.1", port)).unwrap(); 86 | println!("Server listening on port {}", port); 87 | 88 | let mut wrkr_cnt = 0u32; 89 | for tcp_stream in listener.incoming() { 90 | let (conn_tx, conn_rx) = channel(); 91 | conn_tx.send(welcome_msg.clone()).unwrap(); 92 | json_dest_sender.register(conn_tx).unwrap(); 93 | thread::Builder::new().name(format!("websocket_{}", wrkr_cnt)).spawn(move || { 94 | let tcps = tcp_stream.unwrap(); 95 | let mut tcpr = BufReader::new(tcps.try_clone().unwrap()); 96 | let mut tcpw = BufWriter::new(tcps); 97 | WebSocketWorker.run(&mut tcpr, &mut tcpw, &conn_rx).unwrap(); 98 | }).unwrap(); 99 | wrkr_cnt += 1; 100 | } 101 | })?; 102 | 103 | Ok(UIServer { json_multicast: mc }) 104 | } 105 | 106 | pub fn create_sender(&self) -> io::Result>> { 107 | let (tx, rx) = channel(); 108 | let jb = self.json_multicast.clone(); 109 | thread::Builder::new().name("routes_ui".to_string()).spawn(move || { 110 | loop { 111 | let t: Result, _> = rx.recv(); 112 | match t { 113 | Ok(t) => { 114 | let j: String = json::encode(&*t).unwrap(); 115 | jb.send(Arc::new(j)).unwrap(); 116 | } 117 | Err(_) => panic!("oh shit") 118 | } 119 | } 120 | })?; 121 | Ok(tx) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/multicast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "multicast" 4 | version = "0.0.1" 5 | authors = [ "jfager@gmail.com" ] 6 | -------------------------------------------------------------------------------- /src/multicast/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(mpsc_select)] 2 | 3 | use std::sync::mpsc::{channel, Sender, SendError, Receiver}; 4 | use std::thread; 5 | use std::sync::Arc; 6 | use std::io; 7 | 8 | #[derive(Clone)] 9 | pub struct Multicast { 10 | msg_tx: Sender>, 11 | dest_tx: Sender>> 12 | } 13 | 14 | impl Multicast { 15 | pub fn spawn() -> io::Result> { 16 | let (msg_tx, msg_rx): (Sender>, Receiver>) = channel(); 17 | let (dest_tx, dest_rx): (Sender>>, Receiver>>) = channel(); 18 | thread::Builder::new().name("multicast".to_string()).spawn(move || { 19 | let mut mc_txs = Vec::new(); 20 | let mut to_remove = Vec::new(); 21 | loop { 22 | select!( 23 | dest = dest_rx.recv() => mc_txs.push(dest.unwrap()), 24 | msg = msg_rx.recv() => { 25 | let m = msg.unwrap(); 26 | to_remove.truncate(0); 27 | for (i, mc_tx) in mc_txs.iter().enumerate() { 28 | if mc_tx.send(m.clone()).is_err() { 29 | to_remove.push(i) 30 | } 31 | } 32 | if to_remove.len() > 0 { 33 | //Walk in reverse to avoid changing indices of 34 | //channels to be removed. 35 | for i in to_remove.iter().rev() { 36 | mc_txs.remove(*i); 37 | } 38 | } 39 | } 40 | ) 41 | } 42 | })?; 43 | 44 | Ok(Multicast { msg_tx: msg_tx, dest_tx: dest_tx }) 45 | } 46 | 47 | pub fn send(&self, msg: Arc) -> Result<(), SendError>> { 48 | self.msg_tx.send(msg) 49 | } 50 | 51 | pub fn register(&self, dest: Sender>) -> Result<(), SendError>>> { 52 | self.dest_tx.send(dest) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/pcap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "pcap" 4 | version = "0.0.1" 5 | authors = [ "jfager@gmail.com" ] 6 | -------------------------------------------------------------------------------- /src/pcap/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type="lib"] 2 | 3 | #![feature(libc)] 4 | 5 | extern crate libc; 6 | 7 | pub mod pcap; 8 | pub mod pcapll; 9 | -------------------------------------------------------------------------------- /src/pcap/src/pcap.rs: -------------------------------------------------------------------------------- 1 | use libc::{self,c_char,c_int}; 2 | use std::{ptr, slice}; 3 | use std::ffi::CString; 4 | use pcapll; 5 | 6 | //TODO: http://www.tcpdump.org/linktypes.html 7 | pub type DataLinkType = c_int; 8 | pub const DLT_NULL: DataLinkType = 0; 9 | pub const DLT_ETHERNET: DataLinkType = 1; 10 | pub const DLT_IEEE802_11_RADIO: DataLinkType = 127; 11 | 12 | #[derive(Copy,Clone)] 13 | pub struct PcapSessionBuilder { 14 | p: *mut pcapll::pcap_t, 15 | activated: bool 16 | } 17 | 18 | pub fn list_devices() { 19 | println!("list_devices") 20 | } 21 | 22 | impl PcapSessionBuilder { 23 | 24 | pub fn new_dev(dev: &str) -> Result { 25 | let mut errbuf = Vec::with_capacity(256); 26 | let c_dev = CString::new(dev.as_bytes()).unwrap().as_ptr(); 27 | PcapSessionBuilder::do_new(c_dev, errbuf.as_mut_slice()) 28 | } 29 | 30 | pub fn new() -> Result { 31 | let mut errbuf = Vec::with_capacity(256); 32 | let dev = unsafe { pcapll::pcap_lookupdev(errbuf.as_mut_slice().as_mut_ptr()) }; 33 | if dev.is_null() { 34 | Err("No device available") 35 | } else { 36 | PcapSessionBuilder::do_new(dev as *const c_char, errbuf.as_mut_slice()) 37 | } 38 | } 39 | 40 | fn do_new(dev: *const c_char, errbuf: &mut [c_char]) -> Result { 41 | let p = unsafe { pcapll::pcap_create(dev, errbuf.as_mut_ptr()) }; 42 | if p.is_null() { 43 | Err("Could not initialize device") 44 | } else { 45 | Ok(PcapSessionBuilder { p: p, activated: false }) 46 | } 47 | } 48 | 49 | pub fn buffer_size(&mut self, sz: i32) -> &mut PcapSessionBuilder { 50 | if self.activated { panic!("Session already activated") } 51 | unsafe { pcapll::pcap_set_buffer_size(self.p, sz); } 52 | self 53 | } 54 | 55 | pub fn timeout(&mut self, to: i32) -> &mut PcapSessionBuilder { 56 | if self.activated { panic!("Session already activated") } 57 | unsafe { pcapll::pcap_set_timeout(self.p, to); } 58 | self 59 | } 60 | 61 | pub fn promisc(&mut self, promisc: bool) -> &mut PcapSessionBuilder { 62 | if self.activated { panic!("Session already activated") } 63 | unsafe { pcapll::pcap_set_promisc(self.p, promisc as c_int); } 64 | self 65 | } 66 | 67 | pub fn rfmon(&mut self, rfmon: bool) -> &mut PcapSessionBuilder { 68 | if self.activated { panic!("Session already activated") } 69 | unsafe { pcapll::pcap_set_rfmon(self.p, rfmon as c_int); } 70 | self 71 | } 72 | 73 | pub fn activate(&mut self) -> PcapSession { 74 | if self.activated { panic!("Session already activated") } 75 | unsafe { 76 | let res = pcapll::pcap_activate(self.p); 77 | if res != 0 { 78 | panic!("Could not activate pcap session: {}", res); 79 | } 80 | } 81 | self.activated = true; 82 | PcapSession { p: self.p } 83 | } 84 | } 85 | 86 | #[derive(Copy,Clone)] 87 | pub struct PcapSession { 88 | p: *mut pcapll::pcap_t 89 | } 90 | 91 | impl PcapSession { 92 | pub fn from_file(f: &str) -> PcapSession { 93 | let mut errbuf = Vec::with_capacity(256); 94 | unsafe { 95 | let p = pcapll::pcap_open_offline(CString::new(f.as_bytes()).unwrap().as_ptr(), 96 | errbuf.as_mut_slice().as_mut_ptr()); 97 | PcapSession { p: p } 98 | } 99 | } 100 | 101 | pub fn datalink(&self) -> DataLinkType { 102 | unsafe { pcapll::pcap_datalink(self.p) } 103 | } 104 | 105 | pub fn list_datalinks(&self) -> Vec { 106 | unsafe { 107 | let mut dlt_buf = ptr::null_mut(); 108 | let sz = pcapll::pcap_list_datalinks(self.p, &mut dlt_buf); 109 | let out = slice::from_raw_parts(dlt_buf as *const c_int, sz as usize).to_vec(); 110 | pcapll::pcap_free_datalinks(dlt_buf); 111 | out 112 | } 113 | } 114 | 115 | //TODO: add a return value for success/failure 116 | pub fn next(&self, mut f: F) where F: FnMut(&PcapData) { 117 | let mut head_ptr = ptr::null_mut(); 118 | let mut data_ptr = ptr::null(); 119 | let res = unsafe { pcapll::pcap_next_ex(self.p, &mut head_ptr, &mut data_ptr) }; 120 | match res { 121 | 0 => return, //timed out 122 | 1 => { 123 | let p = PcapData { hdr: head_ptr, dat: data_ptr }; 124 | f(&p); 125 | } 126 | _ => { 127 | panic!("pcap_next_ex panicked with {}, find something better to do than blow up", 128 | res); 129 | } 130 | } 131 | } 132 | } 133 | 134 | 135 | pub struct PcapTimeval(libc::timeval); 136 | 137 | impl PcapTimeval { 138 | pub fn sec(&self) -> i64 { 139 | self.0.tv_sec 140 | } 141 | 142 | pub fn usec(&self) -> i32 { 143 | self.0.tv_usec 144 | } 145 | } 146 | 147 | pub struct PcapData { 148 | hdr: *mut pcapll::Struct_pcap_pkthdr, 149 | dat: *const u8 150 | } 151 | 152 | impl PcapData { 153 | pub fn len(&self) -> u32 { 154 | unsafe { (*self.hdr).len } 155 | } 156 | 157 | pub fn caplen(&self) -> u32 { 158 | unsafe { (*self.hdr).caplen } 159 | } 160 | 161 | pub fn ts(&self) -> PcapTimeval { 162 | unsafe { PcapTimeval((*self.hdr).ts) } 163 | } 164 | 165 | pub fn pkt_ptr(&self) -> *const u8 { 166 | self.dat 167 | } 168 | } 169 | 170 | pub struct PcapDumper { 171 | p: *mut pcapll::Struct_pcap_dumper 172 | } 173 | 174 | impl PcapDumper { 175 | pub fn new(sess: &PcapSession, path: &str) -> PcapDumper { 176 | unsafe { 177 | let p = pcapll::pcap_dump_open(sess.p, CString::new(path.as_bytes()).unwrap().as_ptr()); 178 | PcapDumper { p: p } 179 | } 180 | } 181 | 182 | pub fn dump(&mut self, data: &PcapData) { 183 | unsafe { 184 | pcapll::pcap_dump(self.p as *mut u8, data.hdr, data.dat); 185 | } 186 | } 187 | } 188 | 189 | impl Drop for PcapDumper { 190 | fn drop(&mut self) { 191 | unsafe { 192 | pcapll::pcap_dump_close(self.p); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/pcap/src/pcapll.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | /* initially generated by rust-bindgen */ 4 | 5 | pub type u_char = ::libc::c_uchar; 6 | pub type u_int = ::libc::c_uint; 7 | pub type u_short = ::libc::c_ushort; 8 | pub type size_t = ::libc::size_t; 9 | pub type FILE = ::libc::FILE; 10 | 11 | pub type bpf_int32 = ::libc::c_int; 12 | pub type bpf_u_int32 = u_int; 13 | #[repr(C)] 14 | #[derive(Copy,Clone)] 15 | pub struct Struct_bpf_program { 16 | pub bf_len: u_int, 17 | pub bf_insns: *mut Struct_bpf_insn, 18 | } 19 | impl ::std::default::Default for Struct_bpf_program { 20 | fn default() -> Struct_bpf_program { unsafe { ::std::mem::zeroed() } } 21 | } 22 | #[repr(C)] 23 | #[derive(Copy,Clone)] 24 | pub struct Struct_bpf_insn { 25 | pub code: u_short, 26 | pub jt: u_char, 27 | pub jf: u_char, 28 | pub k: bpf_u_int32, 29 | } 30 | impl ::std::default::Default for Struct_bpf_insn { 31 | fn default() -> Struct_bpf_insn { unsafe { ::std::mem::zeroed() } } 32 | } 33 | pub enum Struct_pcap { } 34 | pub type pcap_t = Struct_pcap; 35 | pub enum Struct_pcap_dumper { } 36 | pub type pcap_dumper_t = Struct_pcap_dumper; 37 | pub type pcap_if_t = Struct_pcap_if; 38 | pub type pcap_addr_t = Struct_pcap_addr; 39 | #[repr(C)] 40 | #[derive(Copy,Clone)] 41 | pub struct Struct_pcap_file_header { 42 | pub magic: bpf_u_int32, 43 | pub version_major: u_short, 44 | pub version_minor: u_short, 45 | pub thiszone: bpf_int32, 46 | pub sigfigs: bpf_u_int32, 47 | pub snaplen: bpf_u_int32, 48 | pub linktype: bpf_u_int32, 49 | } 50 | impl ::std::default::Default for Struct_pcap_file_header { 51 | fn default() -> Struct_pcap_file_header { 52 | unsafe { ::std::mem::zeroed() } 53 | } 54 | } 55 | pub type Enum_Unnamed1 = ::libc::c_uint; 56 | pub const PCAP_D_INOUT: ::libc::c_uint = 0; 57 | pub const PCAP_D_IN: ::libc::c_uint = 1; 58 | pub const PCAP_D_OUT: ::libc::c_uint = 2; 59 | pub type pcap_direction_t = Enum_Unnamed1; 60 | #[repr(C)] 61 | #[derive(Copy)] 62 | pub struct Struct_pcap_pkthdr { 63 | pub ts: ::libc::timeval, 64 | pub caplen: bpf_u_int32, 65 | pub len: bpf_u_int32, 66 | pub comment: [::libc::c_char; 256usize], 67 | } 68 | 69 | impl Clone for Struct_pcap_pkthdr { 70 | fn clone(&self) -> Struct_pcap_pkthdr { 71 | Struct_pcap_pkthdr { ..*self } 72 | } 73 | } 74 | 75 | 76 | impl ::std::default::Default for Struct_pcap_pkthdr { 77 | fn default() -> Struct_pcap_pkthdr { unsafe { ::std::mem::zeroed() } } 78 | } 79 | #[repr(C)] 80 | #[derive(Copy,Clone)] 81 | pub struct Struct_pcap_stat { 82 | pub ps_recv: u_int, 83 | pub ps_drop: u_int, 84 | pub ps_ifdrop: u_int, 85 | } 86 | impl ::std::default::Default for Struct_pcap_stat { 87 | fn default() -> Struct_pcap_stat { unsafe { ::std::mem::zeroed() } } 88 | } 89 | #[repr(C)] 90 | #[derive(Copy,Clone)] 91 | pub struct Struct_pcap_if { 92 | pub next: *mut Struct_pcap_if, 93 | pub name: *mut ::libc::c_char, 94 | pub description: *mut ::libc::c_char, 95 | pub addresses: *mut Struct_pcap_addr, 96 | pub flags: bpf_u_int32, 97 | } 98 | impl ::std::default::Default for Struct_pcap_if { 99 | fn default() -> Struct_pcap_if { unsafe { ::std::mem::zeroed() } } 100 | } 101 | pub enum Struct_sockaddr { } 102 | #[repr(C)] 103 | #[derive(Copy,Clone)] 104 | pub struct Struct_pcap_addr { 105 | pub next: *mut Struct_pcap_addr, 106 | pub addr: *mut Struct_sockaddr, 107 | pub netmask: *mut Struct_sockaddr, 108 | pub broadaddr: *mut Struct_sockaddr, 109 | pub dstaddr: *mut Struct_sockaddr, 110 | } 111 | impl ::std::default::Default for Struct_pcap_addr { 112 | fn default() -> Struct_pcap_addr { unsafe { ::std::mem::zeroed() } } 113 | } 114 | pub type pcap_handler = 115 | ::std::option::Option ()>; 118 | #[link(name = "pcap")] 119 | extern "C" { 120 | pub fn bpf_validate(f: *const Struct_bpf_insn, len: ::libc::c_int) 121 | -> ::libc::c_int; 122 | pub fn bpf_filter(arg1: *const Struct_bpf_insn, arg2: *const u_char, 123 | arg3: u_int, arg4: u_int) -> u_int; 124 | pub fn pcap_lookupdev(arg1: *mut ::libc::c_char) -> *mut ::libc::c_char; 125 | pub fn pcap_lookupnet(arg1: *const ::libc::c_char, arg2: *mut bpf_u_int32, 126 | arg3: *mut bpf_u_int32, arg4: *mut ::libc::c_char) 127 | -> ::libc::c_int; 128 | pub fn pcap_create(arg1: *const ::libc::c_char, arg2: *mut ::libc::c_char) 129 | -> *mut pcap_t; 130 | pub fn pcap_set_snaplen(arg1: *mut pcap_t, arg2: ::libc::c_int) 131 | -> ::libc::c_int; 132 | pub fn pcap_set_promisc(arg1: *mut pcap_t, arg2: ::libc::c_int) 133 | -> ::libc::c_int; 134 | pub fn pcap_can_set_rfmon(arg1: *mut pcap_t) -> ::libc::c_int; 135 | pub fn pcap_set_rfmon(arg1: *mut pcap_t, arg2: ::libc::c_int) 136 | -> ::libc::c_int; 137 | pub fn pcap_set_timeout(arg1: *mut pcap_t, arg2: ::libc::c_int) 138 | -> ::libc::c_int; 139 | pub fn pcap_set_tstamp_type(arg1: *mut pcap_t, arg2: ::libc::c_int) 140 | -> ::libc::c_int; 141 | pub fn pcap_set_buffer_size(arg1: *mut pcap_t, arg2: ::libc::c_int) 142 | -> ::libc::c_int; 143 | pub fn pcap_activate(arg1: *mut pcap_t) -> ::libc::c_int; 144 | pub fn pcap_apple_set_exthdr(p: *mut pcap_t, arg1: ::libc::c_int) 145 | -> ::libc::c_int; 146 | pub fn pcap_list_tstamp_types(arg1: *mut pcap_t, 147 | arg2: *mut *mut ::libc::c_int) 148 | -> ::libc::c_int; 149 | pub fn pcap_free_tstamp_types(arg1: *mut ::libc::c_int) -> (); 150 | pub fn pcap_tstamp_type_name_to_val(arg1: *const ::libc::c_char) 151 | -> ::libc::c_int; 152 | pub fn pcap_tstamp_type_val_to_name(arg1: ::libc::c_int) 153 | -> *const ::libc::c_char; 154 | pub fn pcap_tstamp_type_val_to_description(arg1: ::libc::c_int) 155 | -> *const ::libc::c_char; 156 | pub fn pcap_open_live(arg1: *const ::libc::c_char, arg2: ::libc::c_int, 157 | arg3: ::libc::c_int, arg4: ::libc::c_int, 158 | arg5: *mut ::libc::c_char) -> *mut pcap_t; 159 | pub fn pcap_open_dead(arg1: ::libc::c_int, arg2: ::libc::c_int) 160 | -> *mut pcap_t; 161 | 162 | pub fn pcap_open_offline(arg1: *const ::libc::c_char, 163 | arg2: *mut ::libc::c_char) -> *mut pcap_t; 164 | pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut ::libc::c_char) 165 | -> *mut pcap_t; 166 | 167 | pub fn pcap_close(arg1: *mut pcap_t) -> (); 168 | pub fn pcap_loop(arg1: *mut pcap_t, arg2: ::libc::c_int, 169 | arg3: pcap_handler, arg4: *mut u_char) -> ::libc::c_int; 170 | pub fn pcap_dispatch(arg1: *mut pcap_t, arg2: ::libc::c_int, 171 | arg3: pcap_handler, arg4: *mut u_char) 172 | -> ::libc::c_int; 173 | pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut Struct_pcap_pkthdr) 174 | -> *const u_char; 175 | pub fn pcap_next_ex(arg1: *mut pcap_t, arg2: *mut *mut Struct_pcap_pkthdr, 176 | arg3: *mut *const u_char) -> ::libc::c_int; 177 | pub fn pcap_breakloop(arg1: *mut pcap_t) -> (); 178 | pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut Struct_pcap_stat) 179 | -> ::libc::c_int; 180 | pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut Struct_bpf_program) 181 | -> ::libc::c_int; 182 | pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) 183 | -> ::libc::c_int; 184 | pub fn pcap_getnonblock(arg1: *mut pcap_t, arg2: *mut ::libc::c_char) 185 | -> ::libc::c_int; 186 | pub fn pcap_setnonblock(arg1: *mut pcap_t, arg2: ::libc::c_int, 187 | arg3: *mut ::libc::c_char) -> ::libc::c_int; 188 | pub fn pcap_inject(arg1: *mut pcap_t, arg2: *const ::libc::c_void, 189 | arg3: size_t) -> ::libc::c_int; 190 | pub fn pcap_sendpacket(arg1: *mut pcap_t, arg2: *const u_char, 191 | arg3: ::libc::c_int) -> ::libc::c_int; 192 | pub fn pcap_statustostr(arg1: ::libc::c_int) -> *const ::libc::c_char; 193 | pub fn pcap_strerror(arg1: ::libc::c_int) -> *const ::libc::c_char; 194 | pub fn pcap_geterr(arg1: *mut pcap_t) -> *mut ::libc::c_char; 195 | pub fn pcap_perror(arg1: *mut pcap_t, arg2: *mut ::libc::c_char) -> (); 196 | pub fn pcap_compile(arg1: *mut pcap_t, arg2: *mut Struct_bpf_program, 197 | arg3: *const ::libc::c_char, arg4: ::libc::c_int, 198 | arg5: bpf_u_int32) -> ::libc::c_int; 199 | pub fn pcap_compile_nopcap(arg1: ::libc::c_int, arg2: ::libc::c_int, 200 | arg3: *mut Struct_bpf_program, 201 | arg4: *const ::libc::c_char, 202 | arg5: ::libc::c_int, arg6: bpf_u_int32) 203 | -> ::libc::c_int; 204 | pub fn pcap_freecode(arg1: *mut Struct_bpf_program) -> (); 205 | pub fn pcap_offline_filter(arg1: *mut Struct_bpf_program, 206 | arg2: *const Struct_pcap_pkthdr, 207 | arg3: *const u_char) -> ::libc::c_int; 208 | pub fn pcap_datalink(arg1: *mut pcap_t) -> ::libc::c_int; 209 | pub fn pcap_datalink_ext(arg1: *mut pcap_t) -> ::libc::c_int; 210 | pub fn pcap_list_datalinks(arg1: *mut pcap_t, 211 | arg2: *mut *mut ::libc::c_int) 212 | -> ::libc::c_int; 213 | pub fn pcap_set_datalink(arg1: *mut pcap_t, arg2: ::libc::c_int) 214 | -> ::libc::c_int; 215 | pub fn pcap_free_datalinks(arg1: *mut ::libc::c_int) -> (); 216 | pub fn pcap_datalink_name_to_val(arg1: *const ::libc::c_char) 217 | -> ::libc::c_int; 218 | pub fn pcap_datalink_val_to_name(arg1: ::libc::c_int) 219 | -> *const ::libc::c_char; 220 | pub fn pcap_datalink_val_to_description(arg1: ::libc::c_int) 221 | -> *const ::libc::c_char; 222 | pub fn pcap_snapshot(arg1: *mut pcap_t) -> ::libc::c_int; 223 | pub fn pcap_is_swapped(arg1: *mut pcap_t) -> ::libc::c_int; 224 | pub fn pcap_major_version(arg1: *mut pcap_t) -> ::libc::c_int; 225 | pub fn pcap_minor_version(arg1: *mut pcap_t) -> ::libc::c_int; 226 | 227 | pub fn pcap_file(arg1: *mut pcap_t) -> *mut FILE; 228 | 229 | pub fn pcap_fileno(arg1: *mut pcap_t) -> ::libc::c_int; 230 | pub fn pcap_dump_open(arg1: *mut pcap_t, arg2: *const ::libc::c_char) 231 | -> *mut pcap_dumper_t; 232 | 233 | pub fn pcap_dump_fopen(arg1: *mut pcap_t, fp: *mut FILE) 234 | -> *mut pcap_dumper_t; 235 | pub fn pcap_dump_file(arg1: *mut pcap_dumper_t) -> *mut FILE; 236 | 237 | pub fn pcap_dump_ftell(arg1: *mut pcap_dumper_t) -> ::libc::c_long; 238 | pub fn pcap_dump_flush(arg1: *mut pcap_dumper_t) -> ::libc::c_int; 239 | pub fn pcap_dump_close(arg1: *mut pcap_dumper_t) -> (); 240 | pub fn pcap_dump(arg1: *mut u_char, arg2: *const Struct_pcap_pkthdr, 241 | arg3: *const u_char) -> (); 242 | pub fn pcap_findalldevs(arg1: *mut *mut pcap_if_t, 243 | arg2: *mut ::libc::c_char) -> ::libc::c_int; 244 | pub fn pcap_freealldevs(arg1: *mut pcap_if_t) -> (); 245 | pub fn pcap_lib_version() -> *const ::libc::c_char; 246 | pub fn bpf_image(arg1: *const Struct_bpf_insn, arg2: ::libc::c_int) 247 | -> *mut ::libc::c_char; 248 | pub fn bpf_dump(arg1: *const Struct_bpf_program, arg2: ::libc::c_int) 249 | -> (); 250 | pub fn pcap_get_selectable_fd(arg1: *mut pcap_t) -> ::libc::c_int; 251 | } 252 | --------------------------------------------------------------------------------