aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlaurent <laurent@tigrou.my.domain>2012-03-27 01:21:50 +0200
committerlaurent <laurent@tigrou.my.domain>2012-03-27 01:21:50 +0200
commit0ab94d648289babfbeade80b0ce2d566e2518ed8 (patch)
tree7ce9850e57cac48078b5fb0469243a64996e67da
downloadglouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.tar.xz
glouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.zip
initial import from my hg repository
-rwxr-xr-xglouglou_ruby/aa_test.rb304
-rwxr-xr-xglouglou_ruby/glouglou_ruby.rb304
-rw-r--r--glouglou_ruby/vars2
-rw-r--r--glougloud/Makefile12
-rw-r--r--glougloud/README1
-rwxr-xr-xglougloud/glougloudbin0 -> 74978 bytes
-rw-r--r--glougloud/glougloud.c464
-rw-r--r--glougloud/glougloud.h72
-rw-r--r--glougloud/glougloud.obin0 -> 24004 bytes
-rw-r--r--glougloud/imsgev.c160
-rw-r--r--glougloud/imsgev.h48
-rw-r--r--glougloud/imsgev.obin0 -> 9212 bytes
-rw-r--r--glougloud/server.c230
-rw-r--r--glougloud/server.obin0 -> 14224 bytes
-rw-r--r--glougloud/user.c838
-rw-r--r--glougloud/user.obin0 -> 35556 bytes
-rw-r--r--glougloud/util.c188
-rw-r--r--glougloud/util.obin0 -> 11692 bytes
18 files changed, 2623 insertions, 0 deletions
diff --git a/glouglou_ruby/aa_test.rb b/glouglou_ruby/aa_test.rb
new file mode 100755
index 0000000..c5d2af8
--- /dev/null
+++ b/glouglou_ruby/aa_test.rb
@@ -0,0 +1,304 @@
+#!/usr/bin/env ruby
+
+# ported from examples/aa_test.cpp in AGG source tree.
+
+top = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
+src = File.join(top, "src")
+$LOAD_PATH.unshift src
+$LOAD_PATH.unshift File.join(src, "lib")
+
+require 'gtk2'
+
+window = Gtk::Window.new
+window.set_default_size(480, 350)
+
+area = Gtk::DrawingArea.new
+
+def stroke_line(context, x1, y1, x2, y2, line_width, dash_length)
+ context.save do
+ yield if block_given?
+ context.move_to(x1, y1)
+ context.line_to(x2, y2)
+ context.set_dash(dash_length) if dash_length > 0
+ context.line_width = line_width
+ context.line_cap = :round
+ context.stroke
+ end
+end
+
+def fill_triangle(context, x1, y1, x2, y2, x3, y3)
+ context.save do
+ yield if block_given?
+ context.triangle(x1, y1, x2, y2, x3, y3)
+ context.fill
+ end
+end
+
+def set_gradient(context, x1, y1, x2, y2, color_stops)
+ pattern = Cairo::LinearPattern.new(x1, y1, x2, y2)
+ color_stops.each do |offset, color|
+ pattern.add_color_stop(offset, color)
+ end
+ context.set_source(pattern)
+end
+
+def stroke_gradient_line(context, x1, y1, x2, y2,
+ line_width, dash_length, color_stops)
+ stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do
+ set_gradient(context, x1, y1, x2, y2, color_stops)
+ end
+end
+
+def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops)
+ fill_triangle(context, x1, y1, x2, y2, x3, y3) do
+ set_gradient(context,
+ x1, y1,
+ x3 - (x3 - x2) / 2.0,
+ y3 - (y3 - y2) / 2.0,
+ color_stops)
+ end
+end
+
+def draw_background_circle(context, w, h)
+ context.set_source_color([1.0, 1.0, 1.0, 0.2])
+
+ cx = w / 2.0
+ cy = h / 2.0
+ radius = [cx, cy].min
+ 1.upto(180) do |i|
+ n = 2 * Math::PI * i / 180.0
+ stroke_line(context,
+ cx + radius * Math.sin(n),
+ cy + radius * Math.cos(n),
+ cx, cy,
+ 1.0, i < 90 ? i : 0)
+ end
+end
+
+def draw_upper_small_circles(context, i)
+ context.circle(18 + i * 4 + 0.5,
+ 33 + 0.5,
+ i / 20.0)
+ context.fill
+
+ context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5,
+ 27 + (i - 1) / 10.0 + 0.5,
+ 0.5)
+ context.fill
+end
+
+def draw_upper_circles(context, i)
+ context.set_source_color(:white)
+ context.circle(20 + i * (i + 1) + 0.5,
+ 20.5,
+ i / 2.0)
+ context.fill
+
+ draw_upper_small_circles(context, i)
+end
+
+def draw_upper_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 20 + i * (i + 1),
+ 40.5,
+ 20 + i * (i + 1) + (i - 1) * 4,
+ 100.5,
+ i,
+ 0,
+ [[0, :white],
+ [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]])
+end
+
+def draw_middle_small_circles(context, i)
+ stroke_gradient_line(context,
+ 17.5 + i * 4,
+ 107,
+ 17.5 + i * 4 + i / 6.66666667,
+ 107,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+
+ stroke_gradient_line(context,
+ 18 + i * 4,
+ 112.5,
+ 18 + i * 4,
+ 112.5 + i / 6.66666667,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+end
+
+def draw_middle_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 21.5,
+ 120 + (i - 1) * 3.1,
+ 52.5,
+ 120 + (i - 1) * 3.1,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 52.5,
+ 118 + i * 3,
+ 83.5,
+ 118 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 0,
+ [[0, :green],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 83.5,
+ 119 + i * 3,
+ 114.5,
+ 119 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 3.0,
+ [[0, :blue],
+ [1, :white]])
+end
+
+def draw_middle_white_lines(context, i)
+ context.set_source_color(:white)
+ stroke_line(context,
+ 125.5, 119.5 + (i + 2) * (i / 2.0),
+ 135.5, 119.5 + (i + 2) * (i / 2.0),
+ i, 0)
+end
+
+def draw_lower_short_lines(context, i)
+ context.set_source_color(:white)
+
+ stroke_line(context,
+ 17.5 + i * 4, 192,
+ 18.5 + i * 4, 192,
+ i / 10.0, 0)
+
+ stroke_line(context,
+ 17.5 + i * 4 + (i - 1) / 10.0, 186,
+ 18.5 + i * 4 + (i - 1) / 10.0, 186,
+ 1.0, 0)
+end
+
+def draw_right_triangles(context, i, w, h)
+ x1 = w - 150
+ x2 = w - 20
+ y_upper = h - 20 - i * (i + 2)
+ y_middle = h - 20 - i * (i + 1.5)
+ y_lower = h - 20 - i * (i + 1)
+ context.save do
+ pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle)
+ pattern.add_color_stop(0, :white)
+ pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25])
+ context.set_source(pattern)
+ context.move_to(x1, y_middle)
+ context.line_to(x2, y_lower)
+ context.line_to(x2, y_upper)
+ context.fill
+ end
+end
+
+def draw_sample_shapes(context, w, h)
+ context.set_source_color(:black)
+ context.paint
+
+ draw_background_circle(context, w, h)
+
+ 1.upto(20) do |i|
+ draw_upper_circles(context, i)
+ draw_upper_gradient_lines(context, i)
+ draw_middle_small_circles(context, i)
+ draw_middle_gradient_lines(context, i)
+ draw_middle_white_lines(context, i) if i <= 10
+ draw_lower_short_lines(context, i)
+ end
+
+ 1.upto(13) do |i|
+ draw_right_triangles(context, i, w, h)
+ end
+end
+
+def draw_random_shapes(context, w, h)
+ srand(123)
+
+ context.set_source_color(:black)
+ context.paint
+
+ n_circles = 20000
+ start = Time.now
+ n_circles.times do |i|
+ context.circle(rand(w), rand(h), rand(20.0) + 1.0)
+ context.set_source_color([rand, rand, rand, 0.5 + rand / 2])
+ context.fill
+ end
+ circle_draw_time = Time.now - start
+
+ n_lines = 2000
+ start = Time.now
+ n_lines.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ stroke_gradient_line(context,
+ x1, y1,
+ x1 + rand(w * 0.5) - w * 0.25,
+ y1 + rand(h * 0.5) - h * 0.25,
+ 10.0, 0,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ line_draw_time = Time.now - start
+
+ n_triangles = 2000
+ start = Time.now
+ n_triangles.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ fill_gradient_triangle(context,
+ x1, y1,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [0.5, [rand, rand, rand, rand]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ triangle_draw_time = Time.now - start
+
+ puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" %
+ [n_circles.to_f / circle_draw_time,
+ n_lines.to_f / line_draw_time,
+ n_triangles.to_f / triangle_draw_time]
+end
+
+random_mode = false
+
+area.add_events(Gdk::Event::BUTTON_PRESS_MASK)
+area.signal_connect("button-press-event") do |widget, event|
+ random_mode = !random_mode
+ widget.queue_draw
+ false
+end
+
+area.signal_connect("expose-event") do |widget, event|
+ context = widget.window.create_cairo_context
+ x, y, w, h = widget.allocation.to_a
+ if random_mode
+ draw_random_shapes(context, w, h)
+ else
+ draw_sample_shapes(context, w, h)
+ end
+ true
+end
+
+window.add(area)
+
+window.show_all
+
+Gtk.main
+
diff --git a/glouglou_ruby/glouglou_ruby.rb b/glouglou_ruby/glouglou_ruby.rb
new file mode 100755
index 0000000..c5d2af8
--- /dev/null
+++ b/glouglou_ruby/glouglou_ruby.rb
@@ -0,0 +1,304 @@
+#!/usr/bin/env ruby
+
+# ported from examples/aa_test.cpp in AGG source tree.
+
+top = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
+src = File.join(top, "src")
+$LOAD_PATH.unshift src
+$LOAD_PATH.unshift File.join(src, "lib")
+
+require 'gtk2'
+
+window = Gtk::Window.new
+window.set_default_size(480, 350)
+
+area = Gtk::DrawingArea.new
+
+def stroke_line(context, x1, y1, x2, y2, line_width, dash_length)
+ context.save do
+ yield if block_given?
+ context.move_to(x1, y1)
+ context.line_to(x2, y2)
+ context.set_dash(dash_length) if dash_length > 0
+ context.line_width = line_width
+ context.line_cap = :round
+ context.stroke
+ end
+end
+
+def fill_triangle(context, x1, y1, x2, y2, x3, y3)
+ context.save do
+ yield if block_given?
+ context.triangle(x1, y1, x2, y2, x3, y3)
+ context.fill
+ end
+end
+
+def set_gradient(context, x1, y1, x2, y2, color_stops)
+ pattern = Cairo::LinearPattern.new(x1, y1, x2, y2)
+ color_stops.each do |offset, color|
+ pattern.add_color_stop(offset, color)
+ end
+ context.set_source(pattern)
+end
+
+def stroke_gradient_line(context, x1, y1, x2, y2,
+ line_width, dash_length, color_stops)
+ stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do
+ set_gradient(context, x1, y1, x2, y2, color_stops)
+ end
+end
+
+def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops)
+ fill_triangle(context, x1, y1, x2, y2, x3, y3) do
+ set_gradient(context,
+ x1, y1,
+ x3 - (x3 - x2) / 2.0,
+ y3 - (y3 - y2) / 2.0,
+ color_stops)
+ end
+end
+
+def draw_background_circle(context, w, h)
+ context.set_source_color([1.0, 1.0, 1.0, 0.2])
+
+ cx = w / 2.0
+ cy = h / 2.0
+ radius = [cx, cy].min
+ 1.upto(180) do |i|
+ n = 2 * Math::PI * i / 180.0
+ stroke_line(context,
+ cx + radius * Math.sin(n),
+ cy + radius * Math.cos(n),
+ cx, cy,
+ 1.0, i < 90 ? i : 0)
+ end
+end
+
+def draw_upper_small_circles(context, i)
+ context.circle(18 + i * 4 + 0.5,
+ 33 + 0.5,
+ i / 20.0)
+ context.fill
+
+ context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5,
+ 27 + (i - 1) / 10.0 + 0.5,
+ 0.5)
+ context.fill
+end
+
+def draw_upper_circles(context, i)
+ context.set_source_color(:white)
+ context.circle(20 + i * (i + 1) + 0.5,
+ 20.5,
+ i / 2.0)
+ context.fill
+
+ draw_upper_small_circles(context, i)
+end
+
+def draw_upper_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 20 + i * (i + 1),
+ 40.5,
+ 20 + i * (i + 1) + (i - 1) * 4,
+ 100.5,
+ i,
+ 0,
+ [[0, :white],
+ [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]])
+end
+
+def draw_middle_small_circles(context, i)
+ stroke_gradient_line(context,
+ 17.5 + i * 4,
+ 107,
+ 17.5 + i * 4 + i / 6.66666667,
+ 107,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+
+ stroke_gradient_line(context,
+ 18 + i * 4,
+ 112.5,
+ 18 + i * 4,
+ 112.5 + i / 6.66666667,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+end
+
+def draw_middle_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 21.5,
+ 120 + (i - 1) * 3.1,
+ 52.5,
+ 120 + (i - 1) * 3.1,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 52.5,
+ 118 + i * 3,
+ 83.5,
+ 118 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 0,
+ [[0, :green],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 83.5,
+ 119 + i * 3,
+ 114.5,
+ 119 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 3.0,
+ [[0, :blue],
+ [1, :white]])
+end
+
+def draw_middle_white_lines(context, i)
+ context.set_source_color(:white)
+ stroke_line(context,
+ 125.5, 119.5 + (i + 2) * (i / 2.0),
+ 135.5, 119.5 + (i + 2) * (i / 2.0),
+ i, 0)
+end
+
+def draw_lower_short_lines(context, i)
+ context.set_source_color(:white)
+
+ stroke_line(context,
+ 17.5 + i * 4, 192,
+ 18.5 + i * 4, 192,
+ i / 10.0, 0)
+
+ stroke_line(context,
+ 17.5 + i * 4 + (i - 1) / 10.0, 186,
+ 18.5 + i * 4 + (i - 1) / 10.0, 186,
+ 1.0, 0)
+end
+
+def draw_right_triangles(context, i, w, h)
+ x1 = w - 150
+ x2 = w - 20
+ y_upper = h - 20 - i * (i + 2)
+ y_middle = h - 20 - i * (i + 1.5)
+ y_lower = h - 20 - i * (i + 1)
+ context.save do
+ pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle)
+ pattern.add_color_stop(0, :white)
+ pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25])
+ context.set_source(pattern)
+ context.move_to(x1, y_middle)
+ context.line_to(x2, y_lower)
+ context.line_to(x2, y_upper)
+ context.fill
+ end
+end
+
+def draw_sample_shapes(context, w, h)
+ context.set_source_color(:black)
+ context.paint
+
+ draw_background_circle(context, w, h)
+
+ 1.upto(20) do |i|
+ draw_upper_circles(context, i)
+ draw_upper_gradient_lines(context, i)
+ draw_middle_small_circles(context, i)
+ draw_middle_gradient_lines(context, i)
+ draw_middle_white_lines(context, i) if i <= 10
+ draw_lower_short_lines(context, i)
+ end
+
+ 1.upto(13) do |i|
+ draw_right_triangles(context, i, w, h)
+ end
+end
+
+def draw_random_shapes(context, w, h)
+ srand(123)
+
+ context.set_source_color(:black)
+ context.paint
+
+ n_circles = 20000
+ start = Time.now
+ n_circles.times do |i|
+ context.circle(rand(w), rand(h), rand(20.0) + 1.0)
+ context.set_source_color([rand, rand, rand, 0.5 + rand / 2])
+ context.fill
+ end
+ circle_draw_time = Time.now - start
+
+ n_lines = 2000
+ start = Time.now
+ n_lines.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ stroke_gradient_line(context,
+ x1, y1,
+ x1 + rand(w * 0.5) - w * 0.25,
+ y1 + rand(h * 0.5) - h * 0.25,
+ 10.0, 0,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ line_draw_time = Time.now - start
+
+ n_triangles = 2000
+ start = Time.now
+ n_triangles.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ fill_gradient_triangle(context,
+ x1, y1,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [0.5, [rand, rand, rand, rand]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ triangle_draw_time = Time.now - start
+
+ puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" %
+ [n_circles.to_f / circle_draw_time,
+ n_lines.to_f / line_draw_time,
+ n_triangles.to_f / triangle_draw_time]
+end
+
+random_mode = false
+
+area.add_events(Gdk::Event::BUTTON_PRESS_MASK)
+area.signal_connect("button-press-event") do |widget, event|
+ random_mode = !random_mode
+ widget.queue_draw
+ false
+end
+
+area.signal_connect("expose-event") do |widget, event|
+ context = widget.window.create_cairo_context
+ x, y, w, h = widget.allocation.to_a
+ if random_mode
+ draw_random_shapes(context, w, h)
+ else
+ draw_sample_shapes(context, w, h)
+ end
+ true
+end
+
+window.add(area)
+
+window.show_all
+
+Gtk.main
+
diff --git a/glouglou_ruby/vars b/glouglou_ruby/vars
new file mode 100644
index 0000000..e7d6c90
--- /dev/null
+++ b/glouglou_ruby/vars
@@ -0,0 +1,2 @@
+export RUBYLIB=`find /usr/local/lib/ruby/gems/1.8/gems/ -type d -path "*/lib" |xargs |tr ' ' ':'`
+export LD_PRELOAD="/usr/lib/libpthread.so.13.1"
diff --git a/glougloud/Makefile b/glougloud/Makefile
new file mode 100644
index 0000000..6def8e7
--- /dev/null
+++ b/glougloud/Makefile
@@ -0,0 +1,12 @@
+PROG = glougloud
+OBJS = glougloud.o server.o user.o util.o imsgev.o
+CFLAGS+=-Wall -g
+LDFLAGS=-lpcap -levent -lutil
+
+all:
+ make $(OBJS)
+ $(CC) $(OBJS) -o $(PROG) $(LDFLAGS)
+
+clean:
+ rm -f $(PROG) *~ *.o
+
diff --git a/glougloud/README b/glougloud/README
new file mode 100644
index 0000000..07edeb6
--- /dev/null
+++ b/glougloud/README
@@ -0,0 +1 @@
+glougloud - glouglou server, for network traffic visualisation in real time
diff --git a/glougloud/glougloud b/glougloud/glougloud
new file mode 100755
index 0000000..bf4f24a
--- /dev/null
+++ b/glougloud/glougloud
Binary files differ
diff --git a/glougloud/glougloud.c b/glougloud/glougloud.c
new file mode 100644
index 0000000..7b86aa4
--- /dev/null
+++ b/glougloud/glougloud.c
@@ -0,0 +1,464 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <pcap.h>
+#include <pcap-int.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include "glougloud.h"
+#include "imsgev.h"
+
+struct server_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+struct user_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+};
+
+// XXX CONF
+#define PCAP_INTERFACE "lo0"
+#define PCAP_SNAPLEN 100
+#define PCAP_FILTER "not port 4430"
+
+static void imsgev_server(struct imsgev *, int, struct imsg *);
+static void imsgev_user(struct imsgev *, int, struct imsg *);
+
+struct server_proc *srv_proc;
+struct user_proc *usr_proc;
+LIST_HEAD(, user) usr_list;
+int net_socket;
+
+void __dead
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-vi]",
+ __progname);
+ exit(1);
+}
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+/*
+ * reimplement pcap_open_live with more restrictions on the bpf fd :
+ * - open device read only
+ * - lock the fd
+ * based on OpenBSD tcpdump, privsep_pcap.c v1.16
+ */
+
+static pcap_t *
+my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms,
+ char *ebuf, u_int dlt, u_int dirfilt)
+{
+ struct bpf_version bv;
+ u_int v;
+ pcap_t *p;
+ char bpf[sizeof "/dev/bpf0000000000"];
+ int fd, n = 0;
+ struct ifreq ifr;
+
+ p = xmalloc(sizeof(*p));
+ bzero(p, sizeof(*p));
+
+ /* priv part */
+
+ do {
+ snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++);
+ fd = open(bpf, O_RDONLY);
+ } while (fd < 0 && errno == EBUSY);
+ if (fd < 0)
+ return NULL;
+
+ v = 32768; /* XXX this should be a user-accessible hook */
+ ioctl(fd, BIOCSBLEN, &v);
+
+ strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) < 0)
+ return NULL;
+
+ if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
+ return NULL;
+
+ if (promisc)
+ /* this is allowed to fail */
+ ioctl(fd, BIOCPROMISC, NULL);
+ if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
+ return NULL;
+
+ /* lock the descriptor */
+ if (ioctl(fd, BIOCLOCK, NULL) < 0)
+ return NULL;
+
+ /* end of priv part */
+
+ /* fd is locked, can only use 'safe' ioctls */
+ if (ioctl(fd, BIOCVERSION, &bv) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+
+ if (bv.bv_major != BPF_MAJOR_VERSION ||
+ bv.bv_minor < BPF_MINOR_VERSION) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "kernel bpf filter out of date");
+ return NULL;
+ }
+
+ p->fd = fd;
+ p->snapshot = slen;
+
+ /* Get the data link layer type. */
+ if (ioctl(fd, BIOCGDLT, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+#if _BSDI_VERSION - 0 >= 199510
+ /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
+ switch (v) {
+
+ case DLT_SLIP:
+ v = DLT_SLIP_BSDOS;
+ break;
+
+ case DLT_PPP:
+ v = DLT_PPP_BSDOS;
+ break;
+ }
+#endif
+ p->linktype = v;
+
+ /* XXX hack from tcpdump */
+ if (p->linktype == DLT_PFLOG && p->snapshot < 160)
+ p->snapshot = 160;
+
+ /* set timeout */
+ if (to_ms != 0) {
+ struct timeval to;
+ to.tv_sec = to_ms / 1000;
+ to.tv_usec = (to_ms * 1000) % 1000000;
+ if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ }
+
+ if (ioctl(fd, BIOCGBLEN, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ p->bufsize = v;
+ p->buffer = (u_char *)malloc(p->bufsize);
+ if (p->buffer == NULL) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ return p;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_in sock_addr;
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct bpf_program bprog;
+ pcap_t *pcap;
+ int loglevel = 0, logpinvalid = 0;
+ int op;
+ int sock_on = 1;
+
+ if (geteuid() != 0)
+ errx(1, "need root privileges");
+
+ while ((op = getopt(argc, argv, "hvi")) != -1) {
+ switch (op) {
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'v':
+ loglevel++;
+ break;
+ case 'i':
+ logpinvalid = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ log_init(loglevel, logpinvalid);
+ event_init();
+
+ net_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (net_socket < 0)
+ fatal("server: socket failed");
+ setsockopt(net_socket, SOL_SOCKET, SO_REUSEADDR,
+ &sock_on, sizeof(sock_on));
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_addr.sin_port = htons(4430);
+ if (bind(net_socket, (struct sockaddr *)&sock_addr,
+ sizeof(sock_addr)) != 0)
+ fatal("server: socket bind failed, %s", strerror(errno));
+ fd_nonblock(net_socket);
+
+ srv_proc = xmalloc(sizeof(struct server_proc));
+ socketpair_prepare(srv_proc->fd);
+ srv_proc->pid = server_init(srv_proc->fd);
+ close(srv_proc->fd[1]);
+ imsgev_init(&srv_proc->iev, srv_proc->fd[0], NULL, imsgev_server);
+
+ pcap = my_pcap_open_live(PCAP_INTERFACE, PCAP_SNAPLEN, 1, PCAP_TO, errbuf, -1, 0);
+ if (pcap == NULL)
+ fatal("capture: pcap_open_live failed on interface %s\n"
+ "with snaplen %d : %s",
+ PCAP_INTERFACE, PCAP_SNAPLEN, errbuf);
+ if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0)
+ fatal("capture: pcap_compile failed with filter %s : %s",
+ PCAP_FILTER, pcap_geterr(pcap));
+ if (pcap_setfilter(pcap, &bprog) < 0)
+ fatal("capture: pcap_setfilter failed : %s",
+ pcap_geterr(pcap));
+
+ usr_proc = xmalloc(sizeof(struct user_proc));
+ socketpair_prepare(usr_proc->fd);
+ usr_proc->pid = user_init(usr_proc->fd, pcap);
+ close(usr_proc->fd[1]);
+ imsgev_init(&usr_proc->iev, usr_proc->fd[0], NULL, imsgev_user);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ log_info("entering event loop");
+ event_dispatch();
+
+ log_info("exiting");
+ exit(0);
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote) {
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+srvconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_srvconn *req;
+ struct imsg_srvconn res;
+ struct imsg_usrconn req2;
+ struct user *usr;
+
+ req = imsg->data;
+
+ if (req->deco) {
+ log_tmp("srvconn deco");
+ usr = finduser(&req->addr);
+ if (!usr)
+ fatal("trying to deco an inexistant user !");
+
+ addrcpy(&req2.addr, &usr->addr);
+ req2.deco = 1;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+
+ return;
+ }
+
+ if (req->addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+ log_tmp("srvconn accepted");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &req->addr);
+ usr->status = USER_REQUESTED;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req2.addr, &req->addr);
+ req2.deco = 0;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+ }
+ else
+ {
+ log_tmp("srvconn refused");
+ res.ok = 0;
+ addrcpy(&res.addr, &req->addr);
+ imsgev_compose(iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res, sizeof(res));
+ }
+
+}
+
+static void
+imsgev_server(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_REQ:
+ srvconn(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev server read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
+static void
+usrconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct user *usr;
+ struct imsg_usrconn *res;
+ struct imsg_srvconn res2;
+
+ res = imsg->data;
+
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("received usrconn result that wasn't initiated !");
+ usr->status = USER_ACCEPTED;
+
+ addrcpy(&res2.addr, &res->addr);
+ res2.ok = 1;
+ imsgev_compose(&srv_proc->iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res2, sizeof(res2));
+}
+
+/* XXX drop imsg when msgbuf->queued is too high ? */
+/* XXX review that func for correctness and security */
+static void
+usrdns(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrdns_req *req;
+ struct imsg_usrdns_res res;
+ struct hostent *ent;
+ int len;
+ char *name;
+ extern int h_errno;
+ struct in_addr addr;
+
+ req = imsg->data;
+
+ res.addr.s_addr = req->addr.s_addr;
+ addr.s_addr = htonl(req->addr.s_addr);
+ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
+ if (ent) {
+ len = strlen(ent->h_name);
+ if (len > DNSNAME_MAX)
+ name = ent->h_name - DNSNAME_MAX; /* keep right part */
+ else
+ name = ent->h_name;
+ strncpy(res.name, name, sizeof(res.name));
+ } else {
+ log_debug("usrdns failed for %x : %s",
+ addr, hstrerror(h_errno));
+ res.name[0] = '\0';
+ }
+
+ imsgev_compose(&usr_proc->iev, IMSG_USRDNS_RES, 0, 0, -1,
+ &res, sizeof(res));
+}
+
+static void
+imsgev_user(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_USRCONN_RES:
+ usrconn(iev, imsg);
+ break;
+ case IMSG_USRDNS_REQ:
+ usrdns(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev user read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
diff --git a/glougloud/glougloud.h b/glougloud/glougloud.h
new file mode 100644
index 0000000..6d1359b
--- /dev/null
+++ b/glougloud/glougloud.h
@@ -0,0 +1,72 @@
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <pcap.h>
+
+#define GLOUGLOUD_USER "_glougloud"
+#define PCAP_COUNT 20
+#define PCAP_TO 300
+#define DNSNAME_MAX 20
+
+/* ipc */
+
+enum imsg_type {
+ IMSG_SRVCONN_REQ,
+ IMSG_SRVCONN_RES,
+ IMSG_USRCONN_REQ,
+ IMSG_USRCONN_RES,
+ IMSG_USRDNS_REQ,
+ IMSG_USRDNS_RES
+};
+
+/* XXX restore the _req _res structs, so that we have less bytes going on
+ * the pipe */
+struct imsg_srvconn {
+ struct sockaddr_in addr;
+ u_short ok;
+ u_short deco;
+};
+struct imsg_usrconn {
+ struct sockaddr_in addr;
+ u_short ok;
+ u_short deco;
+};
+struct imsg_usrdns_req {
+ struct in_addr addr;
+};
+struct imsg_usrdns_res {
+ struct in_addr addr;
+ char name[DNSNAME_MAX];
+};
+
+/* glougloud.c */
+
+extern int net_socket;
+
+/* server.c */
+
+int server_init(int *);
+
+/* user.c */
+
+int user_init(int *, pcap_t *pcap);
+
+/* util.c */
+
+void log_init(int, int);
+void log_pinvalid(const char *, ...);
+void log_tmp(const char *, ...);
+void log_debug(const char *, ...);
+void log_info(const char *, ...);
+void log_warn(const char *, ...);
+void fatal(const char *, ...);
+
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+void fd_nonblock(int);
+void droppriv();
+void addrcpy(struct sockaddr_in *, struct sockaddr_in *);
+void socketpair_prepare(int *);
+
diff --git a/glougloud/glougloud.o b/glougloud/glougloud.o
new file mode 100644
index 0000000..29614eb
--- /dev/null
+++ b/glougloud/glougloud.o
Binary files differ
diff --git a/glougloud/imsgev.c b/glougloud/imsgev.c
new file mode 100644
index 0000000..ba577aa
--- /dev/null
+++ b/glougloud/imsgev.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsgev.h"
+
+void imsgev_add(struct imsgev *);
+void imsgev_dispatch(int, short, void *);
+void imsgev_disconnect(struct imsgev *, int);
+
+void
+imsgev_init(struct imsgev *iev, int fd, void *data,
+ void (*callback)(struct imsgev *, int, struct imsg *))
+{
+ imsg_init(&iev->ibuf, fd);
+ iev->terminate = 0;
+
+ iev->data = data;
+ iev->handler = imsgev_dispatch;
+ iev->callback = callback;
+
+ iev->events = EV_READ;
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsgev_compose(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+ uint32_t pid, int fd, void *data, u_int16_t datalen)
+{
+ int r;
+
+ r = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen);
+ if (r != -1)
+ imsgev_add(iev);
+
+ return (r);
+}
+
+void
+imsgev_close(struct imsgev *iev)
+{
+ iev->terminate = 1;
+ imsgev_add(iev);
+}
+
+void
+imsgev_clear(struct imsgev *iev)
+{
+ event_del(&iev->ev);
+ msgbuf_clear(&iev->ibuf.w);
+ close(iev->ibuf.fd);
+}
+
+void
+imsgev_add(struct imsgev *iev)
+{
+ short events = 0;
+
+ if (!iev->terminate)
+ events = EV_READ;
+ if (iev->ibuf.w.queued || iev->terminate)
+ events |= EV_WRITE;
+
+ /* optimization: skip event_{del/set/add} if already set */
+ if (events == iev->events)
+ return;
+
+ iev->events = events;
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+void
+imsgev_dispatch(int fd, short ev, void *humppa)
+{
+ struct imsgev *iev = humppa;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ iev->events = 0;
+
+ if (ev & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1) {
+ imsgev_disconnect(iev, IMSGEV_EREAD);
+ return;
+ }
+ if (n == 0) {
+ /*
+ * Connection is closed for reading, and we assume
+ * it is also closed for writing, so we error out
+ * if write data is pending.
+ */
+ imsgev_disconnect(iev,
+ (iev->ibuf.w.queued) ? IMSGEV_EWRITE : IMSGEV_DONE);
+ return;
+ }
+ }
+
+ if (ev & EV_WRITE) {
+ /*
+ * We wanted to write data out but the connection is either
+ * closed, or some error occured. Both case are not recoverable
+ * from the imsg perspective, so we treat it as a WRITE error.
+ */
+ if ((n = msgbuf_write(&ibuf->w)) != 0) {
+ imsgev_disconnect(iev, IMSGEV_EWRITE);
+ return;
+ }
+ }
+
+ while (iev->terminate == 0) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1) {
+ imsgev_disconnect(iev, IMSGEV_EIMSG);
+ return;
+ }
+ if (n == 0)
+ break;
+ iev->callback(iev, IMSGEV_IMSG, &imsg);
+ imsg_free(&imsg);
+ }
+
+ if (iev->terminate && iev->ibuf.w.queued == 0) {
+ imsgev_disconnect(iev, IMSGEV_DONE);
+ return;
+ }
+
+ imsgev_add(iev);
+}
+
+void
+imsgev_disconnect(struct imsgev *iev, int code)
+{
+ iev->callback(iev, code, NULL);
+}
diff --git a/glougloud/imsgev.h b/glougloud/imsgev.h
new file mode 100644
index 0000000..5e7419e
--- /dev/null
+++ b/glougloud/imsgev.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __IMSGEV_H__
+#define __IMSGEV_H__
+
+#include <event.h>
+#include <imsg.h>
+
+#define IMSG_LEN(m) ((m)->hdr.len - IMSG_HEADER_SIZE)
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ void *data;
+ short events;
+ int terminate;
+ void (*callback)(struct imsgev *, int, struct imsg *);
+};
+
+#define IMSGEV_IMSG 0
+#define IMSGEV_DONE 1
+#define IMSGEV_EREAD 2
+#define IMSGEV_EWRITE 3
+#define IMSGEV_EIMSG 4
+
+void imsgev_init(struct imsgev *, int, void *, void (*)(struct imsgev *,
+ int, struct imsg *));
+int imsgev_compose(struct imsgev *, u_int16_t, u_int32_t, u_int32_t, int,
+ void *, u_int16_t);
+void imsgev_close(struct imsgev *);
+void imsgev_clear(struct imsgev *);
+
+#endif /* __IMSGEV_H__ */
diff --git a/glougloud/imsgev.o b/glougloud/imsgev.o
new file mode 100644
index 0000000..821cd63
--- /dev/null
+++ b/glougloud/imsgev.o
Binary files differ
diff --git a/glougloud/server.c b/glougloud/server.c
new file mode 100644
index 0000000..196c3a2
--- /dev/null
+++ b/glougloud/server.c
@@ -0,0 +1,230 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+
+#include "glougloud.h"
+#include "imsgev.h"
+
+#define USER_TIMEOUT 300
+#define USERTIMER 10
+
+struct server {
+ struct imsgev iev;
+ struct event ev;
+
+ struct event usrtimer_ev;
+ struct timeval usrtimer_tv;
+ time_t time;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED,
+ USER_REFUSED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+ time_t lastseen;
+};
+
+static void imsgev_main(struct imsgev *, int, struct imsg *);
+static void receive(int, short, void *);
+static void ev_usrtimer(int, short, void *);
+
+struct server *srv;
+LIST_HEAD(, user) usr_list;
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("server: got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+int
+server_init(int fd[2])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int pid;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("server fork");
+ if (pid > 0)
+ return pid;
+
+ setproctitle("server");
+ event_init();
+ close(fd[0]);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ srv = xmalloc(sizeof(struct server));
+ imsgev_init(&srv->iev, fd[1], NULL, &imsgev_main);
+ srv->time = time(NULL);
+
+ event_set(&srv->ev, net_socket, EV_READ|EV_PERSIST, receive, NULL);
+ event_add(&srv->ev, NULL);
+
+ srv->usrtimer_tv.tv_sec = USERTIMER;
+ srv->usrtimer_tv.tv_usec = 0;
+ evtimer_set(&srv->usrtimer_ev, ev_usrtimer, NULL);
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimr failed: %s", strerror(errno));
+
+ droppriv();
+
+ log_info("server: entering event loop");
+ event_dispatch();
+
+ log_info("server: exiting");
+ exit(0);
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote) {
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+srvconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_srvconn *res;
+ struct user *usr;
+
+ res = imsg->data;
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("server: received srvconn response for inexistant connection");
+ if (res->ok) {
+ usr->status = USER_ACCEPTED;
+ } else {
+ usr->status = USER_REFUSED;
+ }
+}
+
+static void
+imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("server: %s got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_RES:
+ srvconn(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+receive(int fd, short why, void *data)
+{
+ struct sockaddr_in remote;
+ struct imsg_srvconn req;
+ socklen_t len;
+ char buf[16384];
+ struct user *usr;
+ int rv;
+
+ len = sizeof(remote);
+ rv = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote, &len);
+ if (rv < 0) {
+ log_warn("server: recvfrom failed");
+ return;
+ }
+ usr = finduser(&remote);
+ if (usr) {
+ switch (usr->status) {
+ case USER_ACCEPTED:
+ log_tmp("server: data for existing user");
+ break;
+ case USER_REQUESTED:
+ log_warn("server: data for not yet accepted user");
+ break;
+ case USER_REFUSED:
+ log_warn("server: data for banned user !");
+ break;
+ }
+ } else {
+ log_debug("server: new user request");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &remote);
+ usr->status = USER_REQUESTED;
+ usr->lastseen = srv->time;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req.addr, &remote);
+ req.deco = 0;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+ }
+}
+
+static void
+ev_usrtimer(int fd, short why, void *data)
+{
+ struct user *usr;
+ struct imsg_srvconn req;
+
+ srv->time = time(NULL);
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ if (srv->time > usr->lastseen + USER_TIMEOUT) {
+ addrcpy(&req.addr, &usr->addr);
+ req.deco = 1;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+ }
+ }
+
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimer failed : %s", strerror(errno));
+}
diff --git a/glougloud/server.o b/glougloud/server.o
new file mode 100644
index 0000000..27fccd3
--- /dev/null
+++ b/glougloud/server.o
Binary files differ
diff --git a/glougloud/user.c b/glougloud/user.c
new file mode 100644
index 0000000..ac024c6
--- /dev/null
+++ b/glougloud/user.c
@@ -0,0 +1,838 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <pcap.h>
+#include <pcap-int.h>
+
+#include "glougloud.h"
+#include "imsgev.h"
+
+/* XXX a user process should be able to have multiple clients, so that we
+ * can send the same data to multiple machines with almost overhead */
+
+#define NULL_HDRLEN 4 // XXX portable ?
+#define NODE_MAX_WITHOUT_TIMEOUT 1000
+#define NODE_TIMEOUT 300 // XXX conf ?
+#define CONN_TIMEOUT 300 // XXX conf ?
+#define CONN_TIMEOUT_UDP 20 // XXX conf ?
+#define CONN_TIMEOUT_ICMP 10 // XXX conf ?
+#define CONN_FREEIDS_COUNT 65536 /* 2^16 as long as freeids are u_int16_t */
+#define CONNTIMER 5 // XXX conf ?
+#define PACKET_VERSION 1
+
+struct node {
+ LIST_ENTRY(node) entry;
+ struct in_addr addr;
+ time_t lastseen;
+ int used;
+ short namelen;
+#define NODENAME_WAITING -1
+#define NODENAME_FAILED -2
+ char *name;
+};
+
+enum connstate {
+ CONNSTATE_ESTABLISHED,
+ CONNSTATE_TCPFIN,
+ CONNSTATE_TCPFIN2
+};
+
+struct conn {
+ LIST_ENTRY(conn) entry;
+ u_int id;
+ enum connstate state;
+ struct node *src;
+ u_int src_port;
+ struct node *dst;
+ u_int dst_port;
+ u_int proto;
+ u_int size;
+ time_t lastseen;
+};
+
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+};
+
+struct capture {
+ int fd;
+ struct imsgev iev;
+
+ pcap_t *pcap;
+ struct event pcap_ev;
+ struct timeval pcap_tv;
+ pcap_handler pcap_handler;
+
+ LIST_HEAD(, conn) conn_list;
+ LIST_HEAD(, node) node_list;
+ int node_count;
+ u_int16_t conn_freeids[CONN_FREEIDS_COUNT];
+ int conn_freeids_ptr;
+ struct event conntimer_ev;
+ struct timeval conntimer_tv;
+ time_t time;
+
+ int pinvalid;
+ int ptruncated;
+};
+
+struct packet {
+ u_int8_t ver;
+ u_int8_t type;
+/* XXX nicer way for _SIZE ... ? */
+#define PACKET_NEWCONN 0
+#define PACKET_NEWCONN_SIZE (2 + sizeof((struct packet *)0)->pdat.newconn)
+#define PACKET_DELCONN 1
+#define PACKET_DELCONN_SIZE (2 + sizeof((struct packet *)0)->pdat.delconn)
+#define PACKET_DATA 2
+#define PACKET_DATA_SIZE (2 + sizeof((struct packet *)0)->pdat.data)
+#define PACKET_NAME 3
+#define PACKET_NAME_SIZE (2 + sizeof((struct packet *)0)->pdat.name)
+#define PACKET_EXTRA_SIZEMAX 256
+
+ union {
+ struct newconn {
+ u_int16_t id;
+ u_int32_t src;
+ u_int32_t dst;
+ u_int8_t proto;
+ u_int8_t size;
+ } newconn;
+ struct delconn {
+ u_int16_t id;
+ } delconn;
+ struct data {
+ u_int16_t connid;
+ u_int8_t size;
+ } data;
+ struct name {
+ u_int32_t addr;
+ u_int8_t len;
+ } name;
+ } pdat;
+#define newconn_id pdat.newconn.id
+#define newconn_src pdat.newconn.src
+#define newconn_dst pdat.newconn.dst
+#define newconn_proto pdat.newconn.proto
+#define newconn_size pdat.newconn.size
+#define delconn_id pdat.delconn.id
+#define data_connid pdat.data.connid
+#define data_size pdat.data.size
+#define name_addr pdat.name.addr
+#define name_len pdat.name.len
+
+ u_char extra[PACKET_EXTRA_SIZEMAX];
+};
+
+struct phandler {
+ pcap_handler f;
+ int type;
+};
+
+static void ip_handle(struct ip *, const u_char *, u_int);
+static void sendto_all(struct packet *, int);
+static struct node *node_add(struct in_addr *);
+static void node_del(struct node *);
+static struct node *node_find(struct in_addr *);
+static void conn_add(struct in_addr *, int, struct in_addr *, int, int, int);
+static void conn_data(struct conn *, int, int);
+static void conn_del(struct conn *);
+static pcap_handler lookup_phandler(int);
+static struct user *finduser(struct sockaddr_in *);
+
+static void phandler_ether(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+static void phandler_loop(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+static void ev_pcap(int, short, void *);
+static void ev_timer(int, short, void *);
+
+static void usrconn(struct imsgev *, struct imsg *);
+static void imsgev_main(struct imsgev *, int, struct imsg *);
+
+static struct phandler phandlers[] = {
+ { phandler_ether, DLT_EN10MB },
+ { phandler_ether, DLT_IEEE802 },
+ { phandler_loop, DLT_LOOP },
+ { NULL, 0 },
+};
+struct capture *cap;
+LIST_HEAD(, user) usr_list;
+int usr_count = 0;
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("user: got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+int
+user_init(int fd[2], pcap_t *pcap)
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int pid, i;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("user fork");
+ if (pid > 0)
+ return pid;
+
+ setproctitle("user");
+ event_init();
+ close(fd[0]);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ cap = xcalloc(1, sizeof(struct capture));
+ cap->fd = fd[1];
+ imsgev_init(&cap->iev, cap->fd, NULL, &imsgev_main);
+ for (i=0; i<CONN_FREEIDS_COUNT-1; i++)
+ cap->conn_freeids[i] = i;
+ cap->time = time(NULL);
+
+ cap->pcap = pcap;
+ cap->pcap_handler = lookup_phandler(pcap_datalink(pcap));
+ cap->pcap_tv.tv_sec = 0;
+ cap->pcap_tv.tv_usec = PCAP_TO;
+ event_set(&cap->pcap_ev, pcap_fileno(cap->pcap),
+ EV_READ, ev_pcap, NULL);
+
+ cap->conntimer_tv.tv_sec = CONNTIMER;
+ cap->conntimer_tv.tv_usec = 0;
+ evtimer_set(&cap->conntimer_ev, ev_timer, NULL);
+ if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1)
+ fatal("user: event_add conntimer failed: %s", strerror(errno));
+
+ droppriv();
+
+ log_info("user: entering event loop");
+ event_dispatch();
+
+ log_info("user: exiting");
+ exit(0);
+}
+
+/*
+ * Parse an IP packet and descide what to do with it.
+ * 'ip' is a pointer the the captured IP packet
+ * 'pend' is a pointer to the end of the captured IP packet
+ * 'wirelen' is the size of the IP packet off the wire
+ */
+#define NOTCAPTURED(v) ((u_char *)v > pend - sizeof(*v))
+#define NOTRECEIVED(v) (wirelen < sizeof(v))
+static void
+ip_handle(struct ip *ip, const u_char *pend, u_int wirelen)
+{
+ u_int len, ip_hlen, off;
+ const u_char *cp;
+ struct tcphdr *tcph;
+ struct udphdr *udph;
+ struct icmp *icmp;
+ struct in_addr src, dst;
+ u_int src_port, dst_port;
+ u_int size, proto, close, response;
+ struct conn *c, *conn;
+
+ if (NOTCAPTURED(ip)) {
+ log_pinvalid("user: ip truncated");
+ cap->ptruncated++;
+ return;
+ }
+
+ if (ip->ip_v != IPVERSION) {
+ log_pinvalid("user: invalid ip version");
+ cap->pinvalid++;
+ return;
+ }
+
+ len = ntohs(ip->ip_len);
+ if (wirelen < len) {
+ log_pinvalid("user: ip too small");
+ cap->pinvalid++;
+ len = wirelen;
+ }
+
+ ip_hlen = ip->ip_hl * 4;
+ if (ip_hlen < sizeof(struct ip) || ip_hlen > len) {
+ log_pinvalid("user: ip_hlen invalid, %d", ip_hlen);
+ cap->pinvalid++;
+ return;
+ }
+ len -= ip_hlen;
+
+ src.s_addr = ntohl(ip->ip_src.s_addr);
+ dst.s_addr = ntohl(ip->ip_dst.s_addr);
+ src_port = 0;
+ dst_port = 0;
+ proto = IPPROTO_IP;
+ size = len;
+ close = 0;
+
+ off = ntohs(ip->ip_off);
+ if ((off & IP_OFFMASK) == 0) {
+ cp = (const u_char *)ip + ip_hlen;
+ switch (ip->ip_p) {
+
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)cp;
+ if (NOTCAPTURED(&tcph->th_flags)) {
+ log_pinvalid("user: tcp truncated");
+ cap->ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*tcph)) {
+ log_pinvalid("user: tcp too small");
+ cap->pinvalid++;
+ return;
+ }
+ src_port = ntohs(tcph->th_sport);
+ dst_port = ntohs(tcph->th_dport);
+ proto = IPPROTO_TCP;
+ size = len - sizeof(*tcph);
+ if ((tcph->th_flags & TH_FIN) &&
+ (tcph->th_flags & TH_ACK))
+ close = 1;
+ break;
+
+ case IPPROTO_UDP:
+ udph = (struct udphdr *)cp;
+ if (NOTCAPTURED(&udph->uh_dport)) {
+ log_pinvalid("user: udp truncated, "
+ "ip %x, udph %x, uh_port %x, pend %x, ip_hlen %d",
+ ip, udph, &udph->uh_dport, pend, ip_hlen);
+ cap->ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*udph)) {
+ log_pinvalid("user: udp too small");
+ cap->pinvalid++;
+ return;
+ }
+ src_port = ntohs(udph->uh_sport);
+ dst_port = ntohs(udph->uh_dport);
+ proto = IPPROTO_UDP;
+ size = len - sizeof(*udph);
+ break;
+
+ case IPPROTO_ICMP:
+ icmp = (struct icmp *)cp;
+ if (NOTRECEIVED(*icmp)) {
+ log_pinvalid("user: icmp too small");
+ cap->pinvalid++;
+ return;
+ }
+ proto = IPPROTO_ICMP;
+ size = len - sizeof(*icmp);
+ break;
+
+ default:
+ log_warn("user: unknown ip protocol !");
+ break;
+ }
+ } else {
+ /*
+ * if this isn't the first frag, we're missing the
+ * next level protocol header.
+ */
+ log_tmp("user: got a fragmented ip packet !");
+ }
+
+ conn = NULL;
+ LIST_FOREACH(c, &cap->conn_list, entry) {
+ if (((c->src->addr.s_addr == src.s_addr &&
+ c->src_port == src_port &&
+ c->dst->addr.s_addr == dst.s_addr &&
+ c->dst_port == dst_port) ||
+ (c->src->addr.s_addr == dst.s_addr &&
+ c->src_port == dst_port &&
+ c->dst->addr.s_addr == src.s_addr &&
+ c->dst_port == src_port)) &&
+ c->proto == proto) {
+ conn = c;
+ if (c->src->addr.s_addr == src.s_addr)
+ response = 0;
+ else
+ response = 1;
+ break;
+ }
+ }
+
+ if (conn) {
+ if (!close) {
+ conn_data(conn, size, response);
+ } else {
+ conn_del(conn);
+ }
+ } else {
+ if (!close) {
+ conn_add(&src, src_port, &dst, dst_port, proto, size);
+ } else {
+ log_warn("user: captured connection close w/o open !");
+ }
+ }
+}
+
+/* XXX all the packets must have data htos */
+static void
+sendto_all(struct packet *p, int size)
+{
+ struct user *usr;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ if (sendto(net_socket, &p, size, 0,
+ (struct sockaddr *)&usr->addr, sizeof(usr->addr)) == -1)
+ log_warn("send_to failed: %s", strerror(errno));
+ }
+}
+
+static struct node *
+node_add(struct in_addr *addr)
+{
+ struct node *n;
+ struct imsg_usrdns_req req;
+
+ log_debug("user: node_add");
+
+ n = xcalloc(1, sizeof(struct node));
+ n->addr.s_addr = addr->s_addr;
+ n->namelen = NODENAME_WAITING;
+ n->lastseen = cap->time;
+ LIST_INSERT_HEAD(&cap->node_list, n, entry);
+ cap->node_count++;
+
+ req.addr.s_addr = addr->s_addr;
+ imsgev_compose(&cap->iev, IMSG_USRDNS_REQ, 0, 0, -1,
+ &req, sizeof(req));
+
+ return n;
+}
+
+static void
+node_del(struct node *n)
+{
+ if (n->used)
+ fatal("user: trying to remove a used node !");
+ log_debug("user: node_del");
+
+ LIST_REMOVE(n, entry);
+ free(n->name);
+ free(n);
+ cap->node_count--;
+}
+
+static struct node *
+node_find(struct in_addr *remote)
+{
+ struct node *n;
+
+ LIST_FOREACH(n, &cap->node_list, entry)
+ if (n->addr.s_addr == remote->s_addr)
+ return n;
+ return NULL;
+}
+
+static void
+conn_add(struct in_addr *src, int src_port, struct in_addr *dst, int dst_port, int proto, int size)
+{
+ struct packet p;
+ struct conn *c;
+ struct node *srcnode;
+ struct node *dstnode;
+ int id;
+
+ log_debug("user: conn_add, %x:%d->%x:%d %d [%d]",
+ src->s_addr, src_port, dst->s_addr, dst_port, proto, size);
+ if (cap->conn_freeids_ptr == CONN_FREEIDS_COUNT) {
+ log_warn("user: out of connection identifiers !");
+ return;
+ }
+
+ id = cap->conn_freeids[cap->conn_freeids_ptr];
+ cap->conn_freeids_ptr++;
+
+ srcnode = node_find(src);
+ if (!srcnode)
+ srcnode = node_add(src);
+ srcnode->used++;
+ dstnode = node_find(dst);
+ if (!dstnode)
+ dstnode = node_add(dst);
+ dstnode->used++;
+
+ c = xmalloc(sizeof(struct conn));
+ c->id = id;
+ c->state = CONNSTATE_ESTABLISHED;
+ c->src = srcnode;
+ c->src_port = src_port;
+ c->dst = dstnode;
+ c->dst_port = dst_port;
+ c->proto = proto;
+ c->size = size;
+ c->lastseen = cap->time;
+ LIST_INSERT_HEAD(&cap->conn_list, c, entry);
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_NEWCONN;
+ p.newconn_id = id;
+ p.newconn_src = htonl(src->s_addr);
+ p.newconn_dst = htonl(dst->s_addr);
+ p.newconn_proto = htons(proto);
+ p.newconn_size = htons(size << 16);
+ sendto_all(&p, PACKET_NEWCONN_SIZE);
+}
+
+static void
+conn_data(struct conn *c, int size, int response)
+{
+ struct packet p;
+
+ log_debug("user: conn_data");
+
+ c->lastseen = cap->time;
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_DATA;
+ p.data_connid = c->id;
+ p.data_size = htons(size << 16 | response); //XXX
+ sendto_all(&p, PACKET_DATA_SIZE);
+}
+
+static void
+conn_del(struct conn *c)
+{
+ struct packet p;
+
+ log_debug("user: conn_del");
+
+ if (c->proto == IPPROTO_TCP) {
+ switch (c->state) {
+ case CONNSTATE_ESTABLISHED:
+ c->state = CONNSTATE_TCPFIN;
+ return;
+ case CONNSTATE_TCPFIN:
+ c->state = CONNSTATE_TCPFIN2;
+ return;
+ case CONNSTATE_TCPFIN2:
+ break;
+ }
+ }
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_DELCONN;
+ p.delconn_id = c->id;
+ sendto_all(&p, PACKET_DELCONN_SIZE);
+
+ cap->conn_freeids_ptr--;
+ cap->conn_freeids[cap->conn_freeids_ptr] = c->id;
+
+ c->src->used--;
+ c->dst->used--;
+
+ LIST_REMOVE(c, entry);
+ free(c);
+}
+
+static pcap_handler
+lookup_phandler(int type)
+{
+ struct phandler *p;
+
+ for (p = phandlers; p->f; ++p) {
+ if (type == p->type)
+ return p->f;
+ }
+ fatal("user: unknown data link type 0x%x", type);
+ /* NOTREACHED */
+ return NULL;
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote)
+{
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+phandler_ether(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ether_header *ep;
+ struct ip *ip;
+ u_short ether_type;
+ const u_char *pend;
+ u_int len;
+
+ log_debug("user: pcap handler ethernet !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ ep = (struct ether_header *)p;
+ pend = p + h->caplen;
+ len = h->len - sizeof(struct ether_header);
+
+ ether_type = ntohs(ep->ether_type);
+ if (ether_type <= ETHERMTU)
+ log_tmp("llc packet !");
+ else {
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ log_tmp("ether IP");
+ ip = (struct ip *)(ep + sizeof(struct ether_header));
+ ip_handle(ip, pend, len);
+ break;
+ default:
+ log_tmp("non ip packet !");
+ break;
+ }
+ }
+}
+
+static void
+phandler_loop(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ip *ip;
+ struct ether_header *ep;
+ u_short ether_type;
+ u_int family;
+ const u_char *pend;
+ u_int len;
+
+ log_debug("user: pcap handler loop !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ pend = p + h->caplen;
+ len = h->len;
+
+ memcpy((char *)&family, (char *)p, sizeof(family));
+ family = ntohl(family);
+ switch (family) {
+ case AF_INET:
+ log_tmp("loop family AF_INET");
+ ip = (struct ip *)(p + NULL_HDRLEN);
+ len -= NULL_HDRLEN;
+ ip_handle(ip, pend, len);
+ break;
+ case AF_LINK:
+ ep = (struct ether_header *)(p + NULL_HDRLEN);
+ ether_type = ntohs(ep->ether_type);
+ if (ether_type <= ETHERMTU)
+ log_tmp("llc packet !");
+ else {
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ log_tmp("loop family AF_LINK IP");
+ ip = (struct ip *)(ep + sizeof(*ep));
+ len -= NULL_HDRLEN + sizeof (*ep);
+ ip_handle(ip, pend, len);
+ break;
+ default:
+ log_tmp("loop non ip packet !");
+ break;
+ }
+ }
+ default:
+ log_tmp("unknown family %x !", family);
+ break;
+ }
+}
+
+static void
+ev_pcap(int fd, short why, void *data)
+{
+ log_tmp("ev_pcap");
+ pcap_dispatch(cap->pcap, PCAP_COUNT, cap->pcap_handler, NULL);
+
+ /* reschedule */
+ if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1)
+ fatal("user: event_add pcap failed : %s", strerror(errno));
+}
+
+static void
+ev_timer(int fd, short why, void *data)
+{
+ struct conn *c;
+ struct node *n;
+ int i, to;
+
+ log_tmp("ev_timer");
+ cap->time = time(NULL);
+
+ i = 0;
+ LIST_FOREACH(c, &cap->conn_list, entry) {
+ switch (c->proto) {
+ case IPPROTO_UDP:
+ to = CONN_TIMEOUT_UDP;
+ break;
+ case IPPROTO_ICMP:
+ to = CONN_TIMEOUT_ICMP;
+ break;
+ default:
+ to = CONN_TIMEOUT;
+ break;
+ }
+ if (cap->time > c->lastseen + to)
+ conn_del(c);
+ else
+ i++;
+ }
+
+ if (cap->node_count > NODE_MAX_WITHOUT_TIMEOUT) {
+ LIST_FOREACH(n, &cap->node_list, entry) {
+ if (n->used == 0 &&
+ cap->time > n->lastseen + NODE_TIMEOUT)
+ node_del(n);
+ }
+ }
+
+ log_tmp("user: ev_timer leaving with %d active connections and %d active nodes", i, cap->node_count);
+ if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1)
+ fatal("user: event_add conntimer failed : %s", strerror(errno));
+}
+
+static void
+usrconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrconn *req;
+ struct imsg_usrconn res;
+ struct user *usr;
+
+ req = imsg->data;
+
+ if (req->deco) {
+ usr = finduser(&req->addr);
+ if (!usr)
+ fatal("user: trying to deco an inexistant user !");
+ LIST_REMOVE(usr, entry);
+ free(usr);
+ usr_count--;
+ if (usr_count == 0)
+ event_del(&cap->pcap_ev);
+
+ return;
+ }
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &req->addr);
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+ if (usr_count == 0) {
+ if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1)
+ fatal("user: event_add failed : %s", strerror(errno));
+ }
+ usr_count++;
+
+ addrcpy(&res.addr, &usr->addr);
+ res.ok = 1;
+ imsgev_compose(&cap->iev, IMSG_USRCONN_RES, 0, 0, -1,
+ &res, sizeof(res));
+}
+
+static void
+usrdns(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrdns_res *res;
+ struct node *n;
+ struct packet p;
+
+ res = imsg->data;
+
+ n = node_find(&res->addr);
+ if (!n)
+ fatal("user: received usrdns response for inexistant node !");
+ if (n->namelen != NODENAME_WAITING)
+ fatal("user: received usrdns response for a nonwaiting node!");
+
+ if (res->name[0] == '\0') {
+ log_debug("user: resolv for %x failed", res->addr.s_addr);
+ n->namelen = NODENAME_FAILED;
+ return;
+ }
+ n->namelen = strnlen(res->name, DNSNAME_MAX);
+ n->name = strndup(res->name, DNSNAME_MAX);
+
+ log_debug("user: sending node name of %x is %s",
+ n->addr.s_addr, n->name);
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_NAME;
+ p.name_addr = htonl(n->addr.s_addr);
+ p.name_len = n->namelen;
+ strncpy(p.extra, n->name, sizeof(p.extra));
+ sendto_all(&p, PACKET_NAME_SIZE + p.name_len);
+}
+
+static void
+imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("user: %s got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_USRCONN_REQ:
+ usrconn(iev, imsg);
+ break;
+ case IMSG_USRDNS_RES:
+ usrdns(iev, imsg);
+ break;
+ default:
+ fatal("user: %s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ /* NOTREACHED */
+ break;
+ }
+}
+
diff --git a/glougloud/user.o b/glougloud/user.o
new file mode 100644
index 0000000..89be7c2
--- /dev/null
+++ b/glougloud/user.o
Binary files differ
diff --git a/glougloud/util.c b/glougloud/util.c
new file mode 100644
index 0000000..83d55a0
--- /dev/null
+++ b/glougloud/util.c
@@ -0,0 +1,188 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "glougloud.h"
+
+FILE *logfile;
+int loglevel;
+int logpinvalid;
+
+#define LOGFILE "/var/log/glougloud"
+#define LOG_FORCED -2
+#define LOG_FATAL -1
+#define LOG_WARN 0
+#define LOG_INFO 1
+#define LOG_DEBUG 2
+
+static void logit(int, const char *, const char *, va_list);
+
+void
+log_init(int level, int pinvalid)
+{
+ logfile = fopen(LOGFILE, "a+");
+ if (!logfile) {
+ printf("cannot open log file %s!\n", LOGFILE);
+ exit(1);
+ }
+ loglevel = level;
+ logpinvalid = pinvalid;
+}
+
+void
+log_tmp(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_FORCED, "XXX ", msg, ap);
+ va_end(ap);
+}
+void
+log_pinvalid(const char *msg, ...)
+{
+ va_list ap;
+
+ if (!logpinvalid)
+ return;
+
+ va_start(ap, msg);
+ logit(LOG_FORCED, "pinvalid: ", msg, ap);
+ va_end(ap);
+}
+void
+log_debug(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_DEBUG, "", msg, ap);
+ va_end(ap);
+}
+void
+log_info(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_INFO, "", msg, ap);
+ va_end(ap);
+}
+void
+log_warn(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_WARN, "", msg, ap);
+ va_end(ap);
+}
+void __dead
+fatal(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_FATAL, "", msg, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/* XXX mpsafe */
+static void
+logit(int level, const char *prefix, const char *msg, va_list ap)
+{
+ time_t clock;
+
+ if (level <= loglevel) {
+ time(&clock);
+ fprintf(logfile, "%d ", (int)clock);
+ vfprintf(logfile, prefix, ap);
+ vfprintf(logfile, msg, ap);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ fatal("xmalloc: zero size");
+ ptr = malloc(size);
+ if (ptr == NULL)
+ fatal("xmalloc: out of memory (allocating %lu bytes)", (u_long) size);
+ return ptr;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ fatal("xcalloc: zero size");
+ ptr = calloc(nmemb, size);
+ if (ptr == NULL)
+ fatal("xcalloc: out of memory (allocating %lu bytes)", (u_long) size);
+ return ptr;
+}
+
+void
+fd_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (rc == -1)
+ log_warn("failed to set fd %i non-blocking", fd);
+}
+
+void
+droppriv()
+{
+ struct passwd *pw;
+
+ pw = getpwnam(GLOUGLOUD_USER);
+ if (!pw)
+ fatal("unknown user %s", GLOUGLOUD_USER);
+ if (chroot(pw->pw_dir) != 0)
+ fatal("unable to chroot");
+ if (chdir("/") != 0)
+ fatal("unable to chdir");
+ if (setgroups(1, &pw->pw_gid) == -1)
+ fatal("setgroups() failed");
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
+ fatal("setresgid failed");
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+ fatal("setresuid() failed");
+ endpwent();
+}
+
+void
+addrcpy(struct sockaddr_in *dst, struct sockaddr_in *src)
+{
+ dst->sin_addr.s_addr = src->sin_addr.s_addr;
+ dst->sin_port = src->sin_port;
+ dst->sin_family = src->sin_family;
+}
+
+void
+socketpair_prepare(int fd[2])
+{
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1)
+ fatal("socketpair_prepare");
+ fd_nonblock(fd[0]);
+ fd_nonblock(fd[1]);
+}
+
diff --git a/glougloud/util.o b/glougloud/util.o
new file mode 100644
index 0000000..403f795
--- /dev/null
+++ b/glougloud/util.o
Binary files differ