├── .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 | 
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 |
23 |
24 |
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 |
--------------------------------------------------------------------------------