mirror of https://github.com/sipwise/rtpengine.git
commit
dc7517121d
@ -0,0 +1,51 @@
|
||||
CC= gcc
|
||||
CFLAGS= -g -Wall `pkg-config --cflags glib-2.0` `pcre-config --cflags` -fno-strict-aliasing
|
||||
CFLAGS+= -I/lib/modules/`uname -r`/build/include/ -I../kernel-module/
|
||||
CFLAGS+= -D_GNU_SOURCE
|
||||
CFLAGS+= -DMEDIAPROXY_VERSION="\"$(shell dpkg-parsechangelog -l../debian/changelog | awk '/^Version: / {print $$2}')\""
|
||||
CFLAGS+= -DMP_PLUGIN_DIR="\"/usr/lib/mediaproxy-ng\""
|
||||
|
||||
ifeq ($(DBG),yes)
|
||||
CFLAGS+= -D__DEBUG=1
|
||||
else
|
||||
CFLAGS+= -O2
|
||||
endif
|
||||
|
||||
LDFLAGS= `pkg-config --libs glib-2.0` `pcre-config --libs`
|
||||
LDFLAGS+= `xmlrpc-c-config client --libs`
|
||||
LDFLAGS+= -ldl -rdynamic
|
||||
|
||||
SRCS= main.c kernel.c poller.c aux.c control.c streambuf.c call.c control_udp.c redis.c
|
||||
OBJS= $(SRCS:.c=.o)
|
||||
|
||||
|
||||
.PHONY: all dep clean tests debug
|
||||
|
||||
all:
|
||||
$(MAKE) mediaproxy-ng
|
||||
|
||||
debug:
|
||||
$(MAKE) DBG=yes all
|
||||
|
||||
tests:
|
||||
$(MAKE) aux-test poller-test
|
||||
|
||||
dep: .depend
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) mediaproxy-ng aux-test poller-test aux-test.o poller-test.o .depend core
|
||||
|
||||
.depend: $(SRCS) Makefile
|
||||
$(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend
|
||||
|
||||
mediaproxy-ng: $(OBJS) .depend
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
aux-test: aux.o aux-test.o .depend
|
||||
$(CC) $(CFLAGS) -o $@ aux-test.o aux.o $(LDFLAGS)
|
||||
|
||||
poller-test: poller.o poller-test.o aux.o .depend
|
||||
$(CC) $(CFLAGS) -o $@ poller-test.o poller.o aux.o $(LDFLAGS)
|
||||
|
||||
|
||||
include .depend
|
@ -0,0 +1,305 @@
|
||||
#include <stdio.h>
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
int test[32] = {
|
||||
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
|
||||
0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff,
|
||||
|
||||
0x10ff, 0x20ff, 0x30ff, 0x40ff, 0x50ff, 0x60ff, 0x70ff, 0x80ff,
|
||||
0x90ff, 0xa0ff, 0xb0ff, 0xc0ff, 0xd0ff, 0xe0ff, 0xf0ff, 0xffff,
|
||||
};
|
||||
|
||||
void exsrx(int x, int exp, unsigned int s, int ex) {
|
||||
int ret = mybsearch(test, s, sizeof(int), &x, 0, sizeof(x), ex);
|
||||
if (ret != exp)
|
||||
fprintf(stderr, "TEST FAILED! params=%u %i; result=%i, expected=%i\n", s, ex, ret, exp);
|
||||
}
|
||||
|
||||
void exsr1(int x, int exp) {
|
||||
exsrx(x, exp, 16, 1);
|
||||
}
|
||||
|
||||
void exsr2(int x, int exp) {
|
||||
exsrx(x, exp, 15, 1);
|
||||
}
|
||||
|
||||
void exsr3(int x, int exp) {
|
||||
exsrx(x, exp, 2, 1);
|
||||
}
|
||||
|
||||
void exsr4(int x, int exp) {
|
||||
exsrx(x, exp, 1, 1);
|
||||
}
|
||||
|
||||
void exsr5(int x, int exp) {
|
||||
exsrx(x, exp, 32, 1);
|
||||
}
|
||||
|
||||
void exsr6(int x, int exp) {
|
||||
exsrx(x, exp, 31, 1);
|
||||
}
|
||||
|
||||
void exsr7(int x, int exp) {
|
||||
exsrx(x, exp, 16, 0);
|
||||
}
|
||||
|
||||
void exsr8(int x, int exp) {
|
||||
exsrx(x, exp, 15, 0);
|
||||
}
|
||||
|
||||
void exsr9(int x, int exp) {
|
||||
exsrx(x, exp, 2, 0);
|
||||
}
|
||||
|
||||
void exsr10(int x, int exp) {
|
||||
exsrx(x, exp, 1, 0);
|
||||
}
|
||||
|
||||
void exsr11(int x, int exp) {
|
||||
exsrx(x, exp, 32, 0);
|
||||
}
|
||||
|
||||
void exsr12(int x, int exp) {
|
||||
exsrx(x, exp, 31, 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
exsr1(0x10, 0);
|
||||
exsr1(0x20, 1);
|
||||
exsr1(0x30, 2);
|
||||
exsr1(0x40, 3);
|
||||
exsr1(0x50, 4);
|
||||
exsr1(0x60, 5);
|
||||
exsr1(0x70, 6);
|
||||
exsr1(0x80, 7);
|
||||
exsr1(0x90, 8);
|
||||
exsr1(0xa0, 9);
|
||||
exsr1(0xb0, 10);
|
||||
exsr1(0xc0, 11);
|
||||
exsr1(0xd0, 12);
|
||||
exsr1(0xe0, 13);
|
||||
exsr1(0xf0, 14);
|
||||
exsr1(0xff, 15);
|
||||
exsr1(0xffff, -1);
|
||||
exsr1(0xfe, -1);
|
||||
|
||||
exsr2(0x10, 0);
|
||||
exsr2(0x20, 1);
|
||||
exsr2(0x30, 2);
|
||||
exsr2(0x40, 3);
|
||||
exsr2(0x50, 4);
|
||||
exsr2(0x60, 5);
|
||||
exsr2(0x70, 6);
|
||||
exsr2(0x80, 7);
|
||||
exsr2(0x90, 8);
|
||||
exsr2(0xa0, 9);
|
||||
exsr2(0xb0, 10);
|
||||
exsr2(0xc0, 11);
|
||||
exsr2(0xd0, 12);
|
||||
exsr2(0xe0, 13);
|
||||
exsr2(0xf0, 14);
|
||||
exsr2(0xff, -1);
|
||||
|
||||
exsr3(0x10, 0);
|
||||
exsr3(0x20, 1);
|
||||
exsr3(0x30, -1);
|
||||
|
||||
exsr4(0x10, 0);
|
||||
exsr4(0x20, -1);
|
||||
|
||||
exsr5(0x10, 0);
|
||||
exsr5(0x20, 1);
|
||||
exsr5(0x30, 2);
|
||||
exsr5(0x40, 3);
|
||||
exsr5(0x50, 4);
|
||||
exsr5(0x60, 5);
|
||||
exsr5(0x70, 6);
|
||||
exsr5(0x80, 7);
|
||||
exsr5(0x90, 8);
|
||||
exsr5(0xa0, 9);
|
||||
exsr5(0xb0, 10);
|
||||
exsr5(0xc0, 11);
|
||||
exsr5(0xd0, 12);
|
||||
exsr5(0xe0, 13);
|
||||
exsr5(0xf0, 14);
|
||||
exsr5(0xff, 15);
|
||||
exsr5(0x10ff, 16);
|
||||
exsr5(0x20ff, 17);
|
||||
exsr5(0x30ff, 18);
|
||||
exsr5(0x40ff, 19);
|
||||
exsr5(0x50ff, 20);
|
||||
exsr5(0x60ff, 21);
|
||||
exsr5(0x70ff, 22);
|
||||
exsr5(0x80ff, 23);
|
||||
exsr5(0x90ff, 24);
|
||||
exsr5(0xa0ff, 25);
|
||||
exsr5(0xb0ff, 26);
|
||||
exsr5(0xc0ff, 27);
|
||||
exsr5(0xd0ff, 28);
|
||||
exsr5(0xe0ff, 29);
|
||||
exsr5(0xf0ff, 30);
|
||||
exsr5(0xffff, 31);
|
||||
exsr5(0xfff3, -1);
|
||||
exsr5(0xffffff, -1);
|
||||
|
||||
exsr6(0x10, 0);
|
||||
exsr6(0x20, 1);
|
||||
exsr6(0x30, 2);
|
||||
exsr6(0x40, 3);
|
||||
exsr6(0x50, 4);
|
||||
exsr6(0x60, 5);
|
||||
exsr6(0x70, 6);
|
||||
exsr6(0x80, 7);
|
||||
exsr6(0x90, 8);
|
||||
exsr6(0xa0, 9);
|
||||
exsr6(0xb0, 10);
|
||||
exsr6(0xc0, 11);
|
||||
exsr6(0xd0, 12);
|
||||
exsr6(0xe0, 13);
|
||||
exsr6(0xf0, 14);
|
||||
exsr6(0xff, 15);
|
||||
exsr6(0x10ff, 16);
|
||||
exsr6(0x20ff, 17);
|
||||
exsr6(0x30ff, 18);
|
||||
exsr6(0x40ff, 19);
|
||||
exsr6(0x50ff, 20);
|
||||
exsr6(0x60ff, 21);
|
||||
exsr6(0x70ff, 22);
|
||||
exsr6(0x80ff, 23);
|
||||
exsr6(0x90ff, 24);
|
||||
exsr6(0xa0ff, 25);
|
||||
exsr6(0xb0ff, 26);
|
||||
exsr6(0xc0ff, 27);
|
||||
exsr6(0xd0ff, 28);
|
||||
exsr6(0xe0ff, 29);
|
||||
exsr6(0xf0ff, 30);
|
||||
exsr6(0xffff, -1);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
exsr7(0x10, 0);
|
||||
exsr7(0x20, 1);
|
||||
exsr7(0x30, 2);
|
||||
exsr7(0x40, 3);
|
||||
exsr7(0x50, 4);
|
||||
exsr7(0x60, 5);
|
||||
exsr7(0x70, 6);
|
||||
exsr7(0x80, 7);
|
||||
exsr7(0x90, 8);
|
||||
exsr7(0xa0, 9);
|
||||
exsr7(0xb0, 10);
|
||||
exsr7(0xc0, 11);
|
||||
exsr7(0xd0, 12);
|
||||
exsr7(0xe0, 13);
|
||||
exsr7(0xf0, 14);
|
||||
exsr7(0xff, 15);
|
||||
exsr7(0xffff, -17);
|
||||
exsr7(0xfe, -16);
|
||||
exsr7(0x00, -1);
|
||||
exsr8(0x05, -1);
|
||||
exsr8(0x15, -2);
|
||||
|
||||
exsr8(0x10, 0);
|
||||
exsr8(0x20, 1);
|
||||
exsr8(0x30, 2);
|
||||
exsr8(0x40, 3);
|
||||
exsr8(0x50, 4);
|
||||
exsr8(0x60, 5);
|
||||
exsr8(0x70, 6);
|
||||
exsr8(0x80, 7);
|
||||
exsr8(0x90, 8);
|
||||
exsr8(0xa0, 9);
|
||||
exsr8(0xb0, 10);
|
||||
exsr8(0xc0, 11);
|
||||
exsr8(0xd0, 12);
|
||||
exsr8(0xe0, 13);
|
||||
exsr8(0xf0, 14);
|
||||
exsr8(0xff, -16);
|
||||
exsr8(0xffff, -16);
|
||||
exsr8(0xef, -15);
|
||||
exsr8(0x00, -1);
|
||||
exsr8(0x05, -1);
|
||||
exsr8(0x15, -2);
|
||||
|
||||
exsr9(0x10, 0);
|
||||
exsr9(0x20, 1);
|
||||
exsr9(0x30, -3);
|
||||
|
||||
exsr10(0x10, 0);
|
||||
exsr10(0x20, -2);
|
||||
|
||||
exsr11(0x10, 0);
|
||||
exsr11(0x20, 1);
|
||||
exsr11(0x30, 2);
|
||||
exsr11(0x40, 3);
|
||||
exsr11(0x50, 4);
|
||||
exsr11(0x60, 5);
|
||||
exsr11(0x70, 6);
|
||||
exsr11(0x80, 7);
|
||||
exsr11(0x90, 8);
|
||||
exsr11(0xa0, 9);
|
||||
exsr11(0xb0, 10);
|
||||
exsr11(0xc0, 11);
|
||||
exsr11(0xd0, 12);
|
||||
exsr11(0xe0, 13);
|
||||
exsr11(0xf0, 14);
|
||||
exsr11(0xff, 15);
|
||||
exsr11(0x10ff, 16);
|
||||
exsr11(0x20ff, 17);
|
||||
exsr11(0x30ff, 18);
|
||||
exsr11(0x40ff, 19);
|
||||
exsr11(0x50ff, 20);
|
||||
exsr11(0x60ff, 21);
|
||||
exsr11(0x70ff, 22);
|
||||
exsr11(0x80ff, 23);
|
||||
exsr11(0x90ff, 24);
|
||||
exsr11(0xa0ff, 25);
|
||||
exsr11(0xb0ff, 26);
|
||||
exsr11(0xc0ff, 27);
|
||||
exsr11(0xd0ff, 28);
|
||||
exsr11(0xe0ff, 29);
|
||||
exsr11(0xf0ff, 30);
|
||||
exsr11(0xffff, 31);
|
||||
exsr11(0xfff3, -16);
|
||||
exsr11(0xffffff, -33);
|
||||
|
||||
exsr12(0x10, 0);
|
||||
exsr12(0x20, 1);
|
||||
exsr12(0x30, 2);
|
||||
exsr12(0x40, 3);
|
||||
exsr12(0x50, 4);
|
||||
exsr12(0x60, 5);
|
||||
exsr12(0x70, 6);
|
||||
exsr12(0x80, 7);
|
||||
exsr12(0x90, 8);
|
||||
exsr12(0xa0, 9);
|
||||
exsr12(0xb0, 10);
|
||||
exsr12(0xc0, 11);
|
||||
exsr12(0xd0, 12);
|
||||
exsr12(0xe0, 13);
|
||||
exsr12(0xf0, 14);
|
||||
exsr12(0xff, 15);
|
||||
exsr12(0x10ff, 16);
|
||||
exsr12(0x20ff, 17);
|
||||
exsr12(0x30ff, 18);
|
||||
exsr12(0x40ff, 19);
|
||||
exsr12(0x50ff, 20);
|
||||
exsr12(0x60ff, 21);
|
||||
exsr12(0x70ff, 22);
|
||||
exsr12(0x80ff, 23);
|
||||
exsr12(0x90ff, 24);
|
||||
exsr12(0xa0ff, 25);
|
||||
exsr12(0xb0ff, 26);
|
||||
exsr12(0xc0ff, 27);
|
||||
exsr12(0xd0ff, 28);
|
||||
exsr12(0xe0ff, 29);
|
||||
exsr12(0xf0ff, 30);
|
||||
exsr12(0xffff, -32);
|
||||
|
||||
printf("all done\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#define BSDB(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define BSDB(x...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int mybsearch(void *base, unsigned int len, unsigned int size, void *key, unsigned int key_off, unsigned int key_size, int exact) {
|
||||
unsigned char *cbase = base;
|
||||
int pos;
|
||||
unsigned char *cur;
|
||||
int res;
|
||||
unsigned int num;
|
||||
|
||||
if (!len) {
|
||||
BSDB("zero length array\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = len / 2;
|
||||
num = pos;
|
||||
num += 3;
|
||||
num /= 2;
|
||||
pos--;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
BSDB("starting pos=%u, num=%u\n", pos, num);
|
||||
|
||||
for (;;) {
|
||||
cur = cbase + (pos * size);
|
||||
res = memcmp(cur + key_off, key, key_size);
|
||||
BSDB("compare=%i\n", res);
|
||||
if (!res)
|
||||
return pos;
|
||||
if (!num) {
|
||||
BSDB("nothing found\n");
|
||||
if (exact)
|
||||
return -1;
|
||||
if (res > 0) /* cur > key */
|
||||
return -1 * pos - 1;
|
||||
return -1 * pos - 2;
|
||||
}
|
||||
|
||||
if (res < 0) { /* cur < key */
|
||||
pos += num;
|
||||
if (pos >= len)
|
||||
pos = len - 1;
|
||||
}
|
||||
else {
|
||||
pos -= num;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
BSDB("new pos=%u\n", pos);
|
||||
|
||||
if (num == 1)
|
||||
num = 0;
|
||||
else {
|
||||
num++;
|
||||
num /= 2;
|
||||
}
|
||||
|
||||
BSDB("new num=%u\n", num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GList *g_list_link(GList *list, GList *el) {
|
||||
el->prev = NULL;
|
||||
el->next = list;
|
||||
if (list)
|
||||
list->prev = el;
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
GQueue *pcre_multi_match(pcre **re, pcre_extra **ree, const char *rex, const char *s, unsigned int num, parse_func f, void *p) {
|
||||
GQueue *q;
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
unsigned int start, len;
|
||||
int ovec[60];
|
||||
int *ov;
|
||||
char **el;
|
||||
unsigned int i;
|
||||
void *ins;
|
||||
|
||||
if (!*re) {
|
||||
*re = pcre_compile(rex, PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
|
||||
if (!*re)
|
||||
return NULL;
|
||||
*ree = pcre_study(*re, 0, &errptr);
|
||||
}
|
||||
|
||||
q = g_queue_new();
|
||||
el = malloc(sizeof(*el) * num);
|
||||
|
||||
for (start = 0, len = strlen(s); pcre_exec(*re, *ree, s + start, len - start, 0, 0, ovec, G_N_ELEMENTS(ovec)) > 0; start += ovec[1]) {
|
||||
for (i = 0; i < num; i++) {
|
||||
ov = ovec + 2 + i*2;
|
||||
el[i] = (ov[0] == -1) ? NULL : g_strndup(s + start + ov[0], ov[1] - ov[0]);
|
||||
}
|
||||
|
||||
if (!f(el, &ins, p))
|
||||
g_queue_push_tail(q, ins);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (el[i])
|
||||
free(el[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(el);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
void strmove(char **d, char **s) {
|
||||
if (*d)
|
||||
free(*d);
|
||||
*d = *s;
|
||||
*s = strdup("");
|
||||
}
|
||||
|
||||
void strdupfree(char **d, const char *s) {
|
||||
if (*d)
|
||||
free(*d);
|
||||
*d = strdup(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2,14,0)
|
||||
|
||||
void g_string_vprintf(GString *string, const gchar *format, va_list args) {
|
||||
char *s;
|
||||
int r;
|
||||
|
||||
r = vasprintf(&s, format, args);
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
g_string_assign(string, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
void g_queue_clear(GQueue *q) {
|
||||
GList *l, *n;
|
||||
|
||||
if (!q)
|
||||
return;
|
||||
|
||||
for (l = q->head; l; l = n) {
|
||||
n = l->next;
|
||||
g_list_free_1(l);
|
||||
}
|
||||
|
||||
q->head = q->tail = NULL;
|
||||
q->length = 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,150 @@
|
||||
#ifndef __AUX_H__
|
||||
#define __AUX_H__
|
||||
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
#include <stdarg.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
|
||||
|
||||
#define OFFSET_OF(t,e) ((unsigned int) (unsigned long) &(((t *) 0)->e))
|
||||
#define ZERO(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
#define IPF "%u.%u.%u.%u"
|
||||
#define IPP(x) ((unsigned char *) (&(x)))[0], ((unsigned char *) (&(x)))[1], ((unsigned char *) (&(x)))[2], ((unsigned char *) (&(x)))[3]
|
||||
#define IP6F "%x:%x:%x:%x:%x:%x:%x:%x"
|
||||
#define IP6P(x) ntohs(((u_int16_t *) (x))[0]), \
|
||||
ntohs(((u_int16_t *) (x))[1]), \
|
||||
ntohs(((u_int16_t *) (x))[2]), \
|
||||
ntohs(((u_int16_t *) (x))[3]), \
|
||||
ntohs(((u_int16_t *) (x))[4]), \
|
||||
ntohs(((u_int16_t *) (x))[5]), \
|
||||
ntohs(((u_int16_t *) (x))[6]), \
|
||||
ntohs(((u_int16_t *) (x))[7])
|
||||
#define D6F "["IP6F"]:%u"
|
||||
#define D6P(x) IP6P((x).sin6_addr.s6_addr), ntohs((x).sin6_port)
|
||||
#define DF IPF ":%u"
|
||||
#define DP(x) IPP((x).sin_addr.s_addr), ntohs((x).sin_port)
|
||||
|
||||
#define BIT_ARRAY_DECLARE(name, size) int name[((size) + sizeof(int) * 8 - 1) / (sizeof(int) * 8)]
|
||||
|
||||
|
||||
|
||||
|
||||
typedef int (*parse_func)(char **, void **, void *);
|
||||
|
||||
int mybsearch(void *, unsigned int, unsigned int, void *, unsigned int, unsigned int, int);
|
||||
GList *g_list_link(GList *, GList *);
|
||||
GQueue *pcre_multi_match(pcre **, pcre_extra **, const char *, const char *, unsigned int, parse_func, void *);
|
||||
void strmove(char **, char **);
|
||||
void strdupfree(char **, const char *);
|
||||
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2,14,0)
|
||||
#define G_QUEUE_INIT { NULL, NULL, 0 }
|
||||
void g_string_vprintf(GString *string, const gchar *format, va_list args);
|
||||
void g_queue_clear(GQueue *);
|
||||
#endif
|
||||
|
||||
|
||||
static inline void nonblock(int fd) {
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
static inline void reuseaddr(int fd) {
|
||||
int one = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
}
|
||||
static inline void ipv6only(int fd, int yn) {
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn));
|
||||
}
|
||||
|
||||
static inline int bit_array_isset(int *name, unsigned int bit) {
|
||||
return name[(bit) / (sizeof(int) * 8)] & (1 << ((bit) % (sizeof(int) * 8)));
|
||||
}
|
||||
|
||||
static inline void bit_array_set(int *name, unsigned int bit) {
|
||||
name[(bit) / (sizeof(int) * 8)] |= 1 << ((bit) % (sizeof(int) * 8));
|
||||
}
|
||||
|
||||
static inline void bit_array_clear(int *name, unsigned int bit) {
|
||||
name[(bit) / (sizeof(int) * 8)] &= ~(1 << ((bit) % (sizeof(int) * 8)));
|
||||
}
|
||||
|
||||
static inline char chrtoupper(char x) {
|
||||
return x & 0xdf;
|
||||
}
|
||||
|
||||
static inline void swap_ptrs(void *a, void *b) {
|
||||
void *t, **aa, **bb;
|
||||
aa = a;
|
||||
bb = b;
|
||||
t = *aa;
|
||||
*aa = *bb;
|
||||
*bb = t;
|
||||
}
|
||||
|
||||
static inline void in4_to_6(struct in6_addr *o, u_int32_t ip) {
|
||||
o->s6_addr32[0] = 0;
|
||||
o->s6_addr32[1] = 0;
|
||||
o->s6_addr32[2] = htonl(0xffff);
|
||||
o->s6_addr32[3] = ip;
|
||||
}
|
||||
|
||||
static inline void smart_ntop(char *o, struct in6_addr *a, size_t len) {
|
||||
const char *r;
|
||||
|
||||
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||
r = inet_ntop(AF_INET, &(a->s6_addr32[3]), o, len);
|
||||
else
|
||||
r = inet_ntop(AF_INET6, a, o, len);
|
||||
|
||||
if (!r)
|
||||
*o = '\0';
|
||||
}
|
||||
|
||||
static inline void smart_ntop_p(char *o, struct in6_addr *a, size_t len) {
|
||||
int l;
|
||||
|
||||
if (IN6_IS_ADDR_V4MAPPED(a)) {
|
||||
if (!inet_ntop(AF_INET, &(a->s6_addr32[3]), o, len))
|
||||
*o = '\0';
|
||||
}
|
||||
else {
|
||||
*o = '[';
|
||||
if (!inet_ntop(AF_INET6, a, o+1, len-2)) {
|
||||
*o = '\0';
|
||||
return;
|
||||
}
|
||||
l = strlen(o);
|
||||
o[l] = ']';
|
||||
o[l+1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static inline int smart_pton(int af, char *src, void *dst) {
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
if (af == AF_INET6) {
|
||||
if (src[0] == '[' && (p = strchr(src, ']'))) {
|
||||
*p = '\0';
|
||||
ret = inet_pton(af, src+1, dst);
|
||||
*p = ']';
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return inet_pton(af, src, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,138 @@
|
||||
#ifndef __CALL_H__
|
||||
#define __CALL_H__
|
||||
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <glib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "control_udp.h"
|
||||
|
||||
struct poller;
|
||||
struct control_stream;
|
||||
|
||||
|
||||
|
||||
struct peer;
|
||||
struct callstream;
|
||||
struct call;
|
||||
struct callmaster;
|
||||
struct redis;
|
||||
|
||||
|
||||
|
||||
|
||||
struct stats {
|
||||
u_int64_t packets;
|
||||
u_int64_t bytes;
|
||||
u_int64_t errors;
|
||||
};
|
||||
|
||||
struct stream {
|
||||
struct in6_addr ip46;
|
||||
u_int16_t port;
|
||||
char *mediatype;
|
||||
enum {
|
||||
DIR_UNKNOWN = 0,
|
||||
DIR_INTERNAL,
|
||||
DIR_EXTERNAL,
|
||||
} direction[2];
|
||||
int num;
|
||||
};
|
||||
struct streamrelay {
|
||||
int fd;
|
||||
int fd_family;
|
||||
struct stream peer;
|
||||
struct stream peer_advertised;
|
||||
u_int16_t localport;
|
||||
unsigned char idx;
|
||||
struct peer *up;
|
||||
struct stats stats;
|
||||
struct stats kstats;
|
||||
time_t last;
|
||||
};
|
||||
struct peer {
|
||||
struct streamrelay rtps[2];
|
||||
char *tag;
|
||||
char *mediatype;
|
||||
char *codec;
|
||||
unsigned char idx;
|
||||
struct callstream *up;
|
||||
int desired_family;
|
||||
int kernelized:1;
|
||||
int filled:1;
|
||||
int confirmed:1;
|
||||
};
|
||||
struct callstream {
|
||||
struct peer peers[2];
|
||||
struct call *call;
|
||||
int num;
|
||||
};
|
||||
|
||||
struct call {
|
||||
struct callmaster *callmaster;
|
||||
|
||||
GQueue *callstreams;
|
||||
|
||||
char *callid;
|
||||
char redis_uuid[37];
|
||||
time_t created;
|
||||
char *calling_agent;
|
||||
char *called_agent;
|
||||
GHashTable *infohash;
|
||||
GHashTable *branches;
|
||||
time_t lookup_done;
|
||||
|
||||
const char *log_info; /* branch */
|
||||
};
|
||||
|
||||
struct callmaster {
|
||||
GHashTable *callhash;
|
||||
u_int16_t lastport;
|
||||
struct stats statsps;
|
||||
struct stats stats;
|
||||
|
||||
struct poller *poller;
|
||||
struct redis *redis;
|
||||
int kernelfd;
|
||||
unsigned int kernelid;
|
||||
u_int32_t ipv4;
|
||||
u_int32_t adv_ipv4;
|
||||
struct in6_addr ipv6;
|
||||
struct in6_addr adv_ipv6;
|
||||
int port_min;
|
||||
int port_max;
|
||||
unsigned int timeout;
|
||||
unsigned int silent_timeout;
|
||||
char *b2b_url;
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct callmaster *callmaster_new(struct poller *);
|
||||
|
||||
|
||||
|
||||
char *call_request(const char **, struct callmaster *);
|
||||
char *call_update_udp(const char **, struct callmaster *);
|
||||
char *call_lookup(const char **, struct callmaster *);
|
||||
char *call_lookup_udp(const char **, struct callmaster *);
|
||||
void call_delete(const char **, struct callmaster *);
|
||||
char *call_delete_udp(const char **, struct callmaster *);
|
||||
|
||||
void calls_status(struct callmaster *, struct control_stream *);
|
||||
|
||||
void calls_dump_redis(struct callmaster *);
|
||||
|
||||
struct call *call_get_or_create(const char *callid, const char *viabranch, struct callmaster *m);
|
||||
void callstream_init(struct callstream *s, struct call *ca, int port1, int port2, int num);
|
||||
void kernelize(struct callstream *c);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,244 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pcre.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
#include "streambuf.h"
|
||||
#include "log.h"
|
||||
#include "call.h"
|
||||
|
||||
static pcre *parse_re;
|
||||
static pcre_extra *parse_ree;
|
||||
|
||||
static void control_stream_closed(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
struct control *c;
|
||||
|
||||
mylog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr));
|
||||
|
||||
c = s->control;
|
||||
|
||||
c->stream_head = g_list_remove_link(c->stream_head, &s->link);
|
||||
|
||||
close(fd);
|
||||
if (poller_del_item(s->poller, fd))
|
||||
abort();
|
||||
|
||||
streambuf_destroy(s->inbuf);
|
||||
streambuf_destroy(s->outbuf);
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
static void control_list(struct control *c, struct control_stream *s) {
|
||||
struct control_stream *i;
|
||||
|
||||
for (i = (void *) c->stream_head; i; i = (void *) i->link.next)
|
||||
streambuf_printf(s->outbuf, DF "\n", DP(s->inaddr));
|
||||
|
||||
streambuf_printf(s->outbuf, "End.\n");
|
||||
}
|
||||
|
||||
|
||||
static int control_stream_parse(struct control_stream *s, char *line) {
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
int ovec[60];
|
||||
int ret;
|
||||
const char **out;
|
||||
struct control *c = s->control;
|
||||
char *output = NULL;
|
||||
|
||||
if (!parse_re) {
|
||||
parse_re = pcre_compile(
|
||||
/* reqtype callid streams ip fromdom fromtype todom totype agent info |reqtype callid info | reqtype */
|
||||
"^(?:(request|lookup)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+info=(\\S*)|(delete)\\s+(\\S+)\\s+info=(\\S*)|(build|version|controls|quit|exit|status))$",
|
||||
PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
|
||||
parse_ree = pcre_study(parse_re, 0, &errptr);
|
||||
}
|
||||
|
||||
ret = pcre_exec(parse_re, parse_ree, line, strlen(line), 0, 0, ovec, G_N_ELEMENTS(ovec));
|
||||
if (ret <= 0) {
|
||||
mylog(LOG_WARNING, "Unable to parse command line from " DF ": %s", DP(s->inaddr), line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mylog(LOG_INFO, "Got valid command from " DF ": %s", DP(s->inaddr), line);
|
||||
|
||||
pcre_get_substring_list(line, ovec, ret, &out);
|
||||
|
||||
|
||||
if (!strcmp(out[RE_TCP_RL_CMD], "request"))
|
||||
output = call_request(out, c->callmaster);
|
||||
else if (!strcmp(out[RE_TCP_RL_CMD], "lookup"))
|
||||
output = call_lookup(out, c->callmaster);
|
||||
else if (!strcmp(out[RE_TCP_D_CMD], "delete"))
|
||||
call_delete(out, c->callmaster);
|
||||
else if (!strcmp(out[RE_TCP_DIV_CMD], "status"))
|
||||
calls_status(c->callmaster, s);
|
||||
else if (!strcmp(out[RE_TCP_DIV_CMD], "build") | !strcmp(out[RE_TCP_DIV_CMD], "version"))
|
||||
streambuf_printf(s->outbuf, "Version: %s\n", MEDIAPROXY_VERSION);
|
||||
else if (!strcmp(out[RE_TCP_DIV_CMD], "controls"))
|
||||
control_list(c, s);
|
||||
else if (!strcmp(out[RE_TCP_DIV_CMD], "quit") || !strcmp(out[RE_TCP_DIV_CMD], "exit"))
|
||||
;
|
||||
|
||||
if (output) {
|
||||
streambuf_write(s->outbuf, output, strlen(output));
|
||||
free(output);
|
||||
}
|
||||
|
||||
pcre_free(out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void control_stream_timer(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
struct poller *o = s->poller;
|
||||
|
||||
if ((o->now - s->inbuf->active) >= 60 || (o->now - s->outbuf->active) >= 60)
|
||||
control_stream_closed(s->fd, s);
|
||||
}
|
||||
|
||||
|
||||
static void control_stream_readable(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
char *line;
|
||||
int ret;
|
||||
|
||||
if (streambuf_readable(s->inbuf))
|
||||
goto close;
|
||||
|
||||
while ((line = streambuf_getline(s->inbuf))) {
|
||||
mylog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line);
|
||||
ret = control_stream_parse(s, line);
|
||||
free(line);
|
||||
if (ret)
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (streambuf_bufsize(s->inbuf) > 1024) {
|
||||
mylog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr));
|
||||
goto close;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
close:
|
||||
control_stream_closed(fd, s);
|
||||
}
|
||||
|
||||
static void control_stream_writeable(int fd, void *p) {
|
||||
struct control_stream *s = p;
|
||||
|
||||
if (streambuf_writeable(s->outbuf))
|
||||
control_stream_closed(fd, s);
|
||||
}
|
||||
|
||||
static void control_closed(int fd, void *p) {
|
||||
abort();
|
||||
}
|
||||
|
||||
static void control_incoming(int fd, void *p) {
|
||||
int nfd;
|
||||
struct control *c = p;
|
||||
struct control_stream *s;
|
||||
struct poller_item i;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sinl;
|
||||
|
||||
sinl = sizeof(sin);
|
||||
nfd = accept(fd, (struct sockaddr *) &sin, &sinl);
|
||||
if (nfd == -1)
|
||||
return;
|
||||
nonblock(nfd);
|
||||
|
||||
mylog(LOG_INFO, "New control connection from " DF, DP(sin));
|
||||
|
||||
s = malloc(sizeof(*s));
|
||||
ZERO(*s);
|
||||
|
||||
ZERO(i);
|
||||
i.fd = nfd;
|
||||
i.closed = control_stream_closed;
|
||||
i.readable = control_stream_readable;
|
||||
i.writeable = control_stream_writeable;
|
||||
i.timer = control_stream_timer;
|
||||
i.ptr = s;
|
||||
if (poller_add_item(c->poller, &i))
|
||||
goto fail;
|
||||
s->fd = nfd;
|
||||
s->control = c;
|
||||
s->poller = c->poller;
|
||||
s->inbuf = streambuf_new(c->poller, nfd);
|
||||
s->outbuf = streambuf_new(c->poller, nfd);
|
||||
memcpy(&s->inaddr, &sin, sizeof(s->inaddr));
|
||||
|
||||
c->stream_head = g_list_link(c->stream_head, &s->link);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
free(s);
|
||||
close(nfd);
|
||||
}
|
||||
|
||||
|
||||
struct control *control_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m) {
|
||||
int fd;
|
||||
struct control *c;
|
||||
struct poller_item i;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
nonblock(fd);
|
||||
reuseaddr(fd);
|
||||
|
||||
ZERO(sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = ip;
|
||||
sin.sin_port = htons(port);
|
||||
if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)))
|
||||
goto fail;
|
||||
|
||||
if (listen(fd, 5))
|
||||
goto fail;
|
||||
|
||||
|
||||
c = malloc(sizeof(*c));
|
||||
ZERO(*c);
|
||||
|
||||
c->fd = fd;
|
||||
c->poller = p;
|
||||
c->callmaster = m;
|
||||
|
||||
ZERO(i);
|
||||
i.fd = fd;
|
||||
i.closed = control_closed;
|
||||
i.readable = control_incoming;
|
||||
i.ptr = c;
|
||||
if (poller_add_item(p, &i))
|
||||
goto fail2;
|
||||
|
||||
return c;
|
||||
|
||||
fail2:
|
||||
free(c);
|
||||
fail:
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
#ifndef __CONTROL_H__
|
||||
#define __CONTROL_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <glib.h>
|
||||
|
||||
#define RE_TCP_RL_CMD 1
|
||||
#define RE_TCP_RL_CALLID 2
|
||||
#define RE_TCP_RL_STREAMS 3
|
||||
#define RE_TCP_RL_IP 4
|
||||
#define RE_TCP_RL_FROMDOM 5
|
||||
#define RE_TCP_RL_FROMTYPE 6
|
||||
#define RE_TCP_RL_TODOM 7
|
||||
#define RE_TCP_RL_TOTYPE 8
|
||||
#define RE_TCP_RL_AGENT 9
|
||||
#define RE_TCP_RL_INFO 10
|
||||
#define RE_TCP_D_CMD 11
|
||||
#define RE_TCP_D_CALLID 12
|
||||
#define RE_TCP_D_INFO 13
|
||||
#define RE_TCP_DIV_CMD 14
|
||||
|
||||
struct poller;
|
||||
struct control;
|
||||
struct streambuf;
|
||||
struct callmaster;
|
||||
|
||||
|
||||
|
||||
struct control_stream {
|
||||
GList link; /* must be first */
|
||||
|
||||
int fd;
|
||||
struct streambuf *inbuf;
|
||||
struct streambuf *outbuf;
|
||||
struct sockaddr_in inaddr;
|
||||
|
||||
struct control *control;
|
||||
struct poller *poller;
|
||||
};
|
||||
|
||||
|
||||
struct control {
|
||||
int fd;
|
||||
|
||||
GList *stream_head;
|
||||
|
||||
struct poller *poller;
|
||||
struct callmaster *callmaster;
|
||||
};
|
||||
|
||||
|
||||
struct control *control_new(struct poller *, u_int32_t, u_int16_t, struct callmaster *);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,236 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pcre.h>
|
||||
#include <glib.h>
|
||||
#include <time.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "control_udp.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
#include "log.h"
|
||||
#include "call.h"
|
||||
|
||||
|
||||
static void control_udp_closed(int fd, void *p) {
|
||||
abort();
|
||||
}
|
||||
|
||||
static void control_udp_incoming(int fd, void *p) {
|
||||
struct control_udp *u = p;
|
||||
int ret, len;
|
||||
char buf[8192];
|
||||
struct sockaddr_in6 sin;
|
||||
socklen_t sin_len;
|
||||
int ovec[100];
|
||||
const char **out;
|
||||
char *reply;
|
||||
struct msghdr mh;
|
||||
struct iovec iov[10];
|
||||
char addr[64];
|
||||
|
||||
sin_len = sizeof(sin);
|
||||
len = recvfrom(fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &sin, &sin_len);
|
||||
if (len <= 0) {
|
||||
mylog(LOG_WARNING, "Error reading from UDP socket");
|
||||
return;
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
smart_ntop_p(addr, &sin.sin6_addr, sizeof(addr));
|
||||
|
||||
ret = pcre_exec(u->parse_re, u->parse_ree, buf, len, 0, 0, ovec, G_N_ELEMENTS(ovec));
|
||||
if (ret <= 0) {
|
||||
ret = pcre_exec(u->fallback_re, NULL, buf, len, 0, 0, ovec, G_N_ELEMENTS(ovec));
|
||||
if (ret <= 0) {
|
||||
mylog(LOG_WARNING, "Unable to parse command line from udp:%s:%u: %s", addr, ntohs(sin.sin6_port), buf);
|
||||
return;
|
||||
}
|
||||
|
||||
mylog(LOG_WARNING, "Failed to properly parse UDP command line '%s' from %s:%u, using fallback RE", buf, addr, ntohs(sin.sin6_port));
|
||||
|
||||
pcre_get_substring_list(buf, ovec, ret, &out);
|
||||
|
||||
ZERO(mh);
|
||||
mh.msg_name = &sin;
|
||||
mh.msg_namelen = sizeof(sin);
|
||||
mh.msg_iov = iov;
|
||||
|
||||
iov[0].iov_base = (void *) out[RE_UDP_COOKIE];
|
||||
iov[0].iov_len = strlen(out[RE_UDP_COOKIE]);
|
||||
if (out[RE_UDP_UL_CMD] && (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U' || chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')) {
|
||||
iov[1].iov_base = (void *) out[RE_UDP_UL_CALLID];
|
||||
iov[1].iov_len = strlen(out[RE_UDP_UL_CALLID]);
|
||||
iov[2].iov_base = (void *) out[RE_UDP_UL_FLAGS];
|
||||
iov[2].iov_len = strlen(out[RE_UDP_UL_FLAGS]);
|
||||
iov[3].iov_base = "\n";
|
||||
iov[3].iov_len = 1;
|
||||
mh.msg_iovlen = 4;
|
||||
}
|
||||
else {
|
||||
iov[1].iov_base = " E8\n";
|
||||
iov[1].iov_len = 4;
|
||||
mh.msg_iovlen = 2;
|
||||
}
|
||||
|
||||
sendmsg(fd, &mh, 0);
|
||||
|
||||
pcre_free(out);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mylog(LOG_INFO, "Got valid command from udp:%s:%u: %s", addr, ntohs(sin.sin6_port), buf);
|
||||
|
||||
pcre_get_substring_list(buf, ovec, ret, &out);
|
||||
|
||||
if (u->poller->now - u->oven_time >= 30) {
|
||||
g_hash_table_remove_all(u->stale_cookies);
|
||||
#if GLIB_CHECK_VERSION(2,14,0)
|
||||
g_string_chunk_clear(u->stale_chunks);
|
||||
swap_ptrs(&u->stale_chunks, &u->fresh_chunks);
|
||||
#else
|
||||
g_string_chunk_free(u->stale_chunks);
|
||||
u->stale_chunks = u->fresh_chunks;
|
||||
u->fresh_chunks = g_string_chunk_new(4 * 1024);
|
||||
#endif
|
||||
swap_ptrs(&u->stale_cookies, &u->fresh_cookies);
|
||||
u->oven_time = u->poller->now; /* baked new cookies! */
|
||||
}
|
||||
|
||||
/* XXX better hashing */
|
||||
reply = g_hash_table_lookup(u->fresh_cookies, out[RE_UDP_COOKIE]);
|
||||
if (!reply)
|
||||
reply = g_hash_table_lookup(u->stale_cookies, out[RE_UDP_COOKIE]);
|
||||
if (reply) {
|
||||
mylog(LOG_INFO, "Detected command from udp:%s:%u as a duplicate", addr, ntohs(sin.sin6_port));
|
||||
sendto(fd, reply, strlen(reply), 0, (struct sockaddr *) &sin, sin_len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U')
|
||||
reply = call_update_udp(out, u->callmaster);
|
||||
else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')
|
||||
reply = call_lookup_udp(out, u->callmaster);
|
||||
else if (chrtoupper(out[RE_UDP_D_CMD][0]) == 'D')
|
||||
reply = call_delete_udp(out, u->callmaster);
|
||||
else if (chrtoupper(out[RE_UDP_V_CMD][0]) == 'V') {
|
||||
ZERO(mh);
|
||||
mh.msg_name = &sin;
|
||||
mh.msg_namelen = sizeof(sin);
|
||||
mh.msg_iov = iov;
|
||||
mh.msg_iovlen = 2;
|
||||
|
||||
iov[0].iov_base = (void *) out[RE_UDP_COOKIE];
|
||||
iov[0].iov_len = strlen(out[RE_UDP_COOKIE]);
|
||||
iov[1].iov_base = " ";
|
||||
iov[1].iov_len = 1;
|
||||
|
||||
if (chrtoupper(out[RE_UDP_V_FLAGS][0]) == 'F') {
|
||||
ret = 0;
|
||||
if (!strcmp(out[RE_UDP_V_PARMS], "20040107"))
|
||||
ret = 1;
|
||||
else if (!strcmp(out[RE_UDP_V_PARMS], "20050322"))
|
||||
ret = 1;
|
||||
else if (!strcmp(out[RE_UDP_V_PARMS], "20060704"))
|
||||
ret = 1;
|
||||
iov[2].iov_base = ret ? "1\n" : "0\n";
|
||||
iov[2].iov_len = 2;
|
||||
mh.msg_iovlen++;
|
||||
}
|
||||
else {
|
||||
iov[2].iov_base = "20040107\n";
|
||||
iov[2].iov_len = 9;
|
||||
mh.msg_iovlen++;
|
||||
}
|
||||
sendmsg(fd, &mh, 0);
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
sendto(fd, reply, strlen(reply), 0, (struct sockaddr *) &sin, sin_len);
|
||||
g_hash_table_insert(u->fresh_cookies, g_string_chunk_insert(u->fresh_chunks, out[RE_UDP_COOKIE]),
|
||||
g_string_chunk_insert(u->fresh_chunks, reply));
|
||||
free(reply);
|
||||
}
|
||||
|
||||
out:
|
||||
pcre_free(out);
|
||||
}
|
||||
|
||||
struct control_udp *control_udp_new(struct poller *p, struct in6_addr ip, u_int16_t port, struct callmaster *m) {
|
||||
int fd;
|
||||
struct control_udp *c;
|
||||
struct poller_item i;
|
||||
struct sockaddr_in6 sin;
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
|
||||
if (!p || !m)
|
||||
return NULL;
|
||||
|
||||
fd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
nonblock(fd);
|
||||
reuseaddr(fd);
|
||||
ipv6only(fd, 0);
|
||||
|
||||
ZERO(sin);
|
||||
sin.sin6_family = AF_INET6;
|
||||
sin.sin6_addr = ip;
|
||||
sin.sin6_port = htons(port);
|
||||
if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)))
|
||||
goto fail;
|
||||
|
||||
|
||||
c = malloc(sizeof(*c));
|
||||
ZERO(*c);
|
||||
|
||||
c->fd = fd;
|
||||
c->poller = p;
|
||||
c->callmaster = m;
|
||||
c->fresh_cookies = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
c->stale_cookies = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
c->fresh_chunks = g_string_chunk_new(4 * 1024);
|
||||
c->stale_chunks = g_string_chunk_new(4 * 1024);
|
||||
c->oven_time = p->now;
|
||||
c->parse_re = pcre_compile(
|
||||
/* cookie cmd flags callid viabranch:5 */
|
||||
"^(\\S+)\\s+(?:([ul])(\\S*)\\s+([^;]+)(?:;(\\S+))?\\s+" \
|
||||
/* addr4 addr6:7 */
|
||||
"(?:([\\d.]+)|([\\da-f:]+(?::ffff:[\\d.]+)?))" \
|
||||
/* port fromtag num totag:11 */
|
||||
"\\s+(\\d+)\\s+(\\S+?);(\\d+)(?:\\s+(\\S+?);\\d+(?:\\s+.*)?)?\r?\n?$" \
|
||||
/* "d" flags callid viabranch fromtag totag:17 */
|
||||
"|(d)(\\S*)\\s+([^;\\s]+)(?:;(\\S+))?\\s+(\\S+?)(?:\\s+(\\S+?))?\r?\n?$" \
|
||||
/* v flags params:20 */
|
||||
"|(v)(\\S*)(?:\\s+(\\S+))?)",
|
||||
PCRE_DOLLAR_ENDONLY | PCRE_DOTALL | PCRE_CASELESS, &errptr, &erroff, NULL);
|
||||
c->parse_ree = pcre_study(c->parse_re, 0, &errptr);
|
||||
/* cookie cmd flags callid addr port */
|
||||
c->fallback_re = pcre_compile("^(\\S+)(?:\\s+(\\S)\\S*\\s+\\S+(\\s+\\S+)(\\s+\\S+))?", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL | PCRE_CASELESS, &errptr, &erroff, NULL);
|
||||
|
||||
if (!c->parse_re || !c->fallback_re)
|
||||
goto fail2;
|
||||
|
||||
ZERO(i);
|
||||
i.fd = fd;
|
||||
i.closed = control_udp_closed;
|
||||
i.readable = control_udp_incoming;
|
||||
i.ptr = c;
|
||||
if (poller_add_item(p, &i))
|
||||
goto fail2;
|
||||
|
||||
return c;
|
||||
|
||||
fail2:
|
||||
free(c);
|
||||
fail:
|
||||
close(fd);
|
||||
return NULL;
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
#ifndef __CONTROL_UDP_H__
|
||||
#define __CONTROL_UDP_H__
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <pcre.h>
|
||||
#include <glib.h>
|
||||
#include <time.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define RE_UDP_COOKIE 1
|
||||
#define RE_UDP_UL_CMD 2
|
||||
#define RE_UDP_UL_FLAGS 3
|
||||
#define RE_UDP_UL_CALLID 4
|
||||
#define RE_UDP_UL_VIABRANCH 5
|
||||
#define RE_UDP_UL_ADDR4 6
|
||||
#define RE_UDP_UL_ADDR6 7
|
||||
#define RE_UDP_UL_PORT 8
|
||||
#define RE_UDP_UL_FROMTAG 9
|
||||
#define RE_UDP_UL_NUM 10
|
||||
#define RE_UDP_UL_TOTAG 11
|
||||
#define RE_UDP_D_CMD 12
|
||||
#define RE_UDP_D_FLAGS 13
|
||||
#define RE_UDP_D_CALLID 14
|
||||
#define RE_UDP_D_VIABRANCH 15
|
||||
#define RE_UDP_D_FROMTAG 16
|
||||
#define RE_UDP_D_TOTAG 17
|
||||
#define RE_UDP_V_CMD 18
|
||||
#define RE_UDP_V_FLAGS 19
|
||||
#define RE_UDP_V_PARMS 20
|
||||
|
||||
struct poller;
|
||||
struct callmaster;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct control_udp {
|
||||
int fd;
|
||||
|
||||
struct poller *poller;
|
||||
struct callmaster *callmaster;
|
||||
|
||||
pcre *parse_re;
|
||||
pcre_extra *parse_ree;
|
||||
pcre *fallback_re;
|
||||
GHashTable *fresh_cookies, *stale_cookies;
|
||||
GStringChunk *fresh_chunks, *stale_chunks;
|
||||
time_t oven_time;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct control_udp *control_udp_new(struct poller *, struct in6_addr, u_int16_t, struct callmaster *);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,142 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "xt_MEDIAPROXY.h"
|
||||
|
||||
#include "aux.h"
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
#define PREFIX "/proc/mediaproxy"
|
||||
#else
|
||||
#define PREFIX "/tmp/mediaproxy"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int kernel_create_table(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
fd = open(PREFIX "/control", O_WRONLY | O_TRUNC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
sprintf(str, "add %u\n", id);
|
||||
i = write(fd, str, strlen(str));
|
||||
if (i == -1)
|
||||
goto fail;
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int kernel_open_table(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
struct mediaproxy_message msg;
|
||||
int i;
|
||||
|
||||
sprintf(str, PREFIX "/%u/control", id);
|
||||
fd = open(str, O_WRONLY | O_TRUNC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = MMG_NOOP;
|
||||
i = write(fd, &msg, sizeof(msg));
|
||||
if (i <= 0)
|
||||
goto fail;
|
||||
|
||||
return fd;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void addr_copy(struct mp_address *mp, struct ip_port *ap) {
|
||||
mp->family = ap->family;
|
||||
mp->port = ap->port;
|
||||
switch (mp->family) {
|
||||
case AF_INET:
|
||||
mp->ipv4 = ap->ipv4;
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(mp->ipv6, &ap->ipv6, 16);
|
||||
break;
|
||||
default:
|
||||
/* XXX panic */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int kernel_add_stream(int fd, struct kernel_stream *info, int update) {
|
||||
struct mediaproxy_message msg;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = update ? MMG_UPDATE : MMG_ADD;
|
||||
msg.target.target_port = info->local_port;
|
||||
addr_copy(&msg.target.src_addr, &info->src);
|
||||
addr_copy(&msg.target.dst_addr, &info->dest);
|
||||
addr_copy(&msg.target.mirror_addr, &info->mirror);
|
||||
msg.target.tos = info->tos;
|
||||
|
||||
return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
int kernel_del_stream(int fd, u_int16_t p) {
|
||||
struct mediaproxy_message msg;
|
||||
|
||||
ZERO(msg);
|
||||
msg.cmd = MMG_DEL;
|
||||
msg.target.target_port = p;
|
||||
|
||||
return write(fd, &msg, sizeof(msg)) <= 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
GList *kernel_list(unsigned int id) {
|
||||
char str[64];
|
||||
int fd;
|
||||
struct mediaproxy_list_entry *buf;
|
||||
GList *li = NULL;
|
||||
int ret;
|
||||
|
||||
sprintf(str, PREFIX "/%u/blist", id);
|
||||
fd = open(str, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
|
||||
for (;;) {
|
||||
buf = g_slice_alloc(sizeof(*buf));
|
||||
ret = read(fd, buf, sizeof(*buf));
|
||||
if (ret != sizeof(*buf))
|
||||
break;
|
||||
li = g_list_prepend(li, buf);
|
||||
}
|
||||
|
||||
g_slice_free1(sizeof(*buf), buf);
|
||||
close(fd);
|
||||
|
||||
return li;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
#ifndef __KERNEL_H__
|
||||
#define __KERNEL_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
|
||||
struct ip_port {
|
||||
int family;
|
||||
union {
|
||||
u_int32_t ipv4;
|
||||
struct in6_addr ipv6;
|
||||
};
|
||||
u_int16_t port;
|
||||
};
|
||||
|
||||
struct kernel_stream {
|
||||
u_int16_t local_port;
|
||||
struct ip_port src;
|
||||
struct ip_port dest;
|
||||
struct ip_port mirror;
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int kernel_create_table(unsigned int);
|
||||
int kernel_open_table(unsigned int);
|
||||
|
||||
int kernel_add_stream(int, struct kernel_stream *, int);
|
||||
int kernel_del_stream(int, u_int16_t);
|
||||
GList *kernel_list(unsigned int);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,12 @@
|
||||
#ifndef __LOG_H__
|
||||
#define __LOG_H__
|
||||
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
|
||||
#define mylog(x,y...) syslog(x,y)
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,370 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <glib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "poller.h"
|
||||
#include "control.h"
|
||||
#include "control_udp.h"
|
||||
#include "aux.h"
|
||||
#include "log.h"
|
||||
#include "call.h"
|
||||
#include "kernel.h"
|
||||
#include "redis.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define die(x...) do { fprintf(stderr, x); exit(-1); } while(0)
|
||||
#define dlresolve(m,n) do { \
|
||||
n = dlsym(m, "mod_" #n); \
|
||||
if (!n) \
|
||||
die("Failed to resolve symbol from plugin: %s\n", #n); \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static char *pidfile;
|
||||
static gboolean foreground;
|
||||
static u_int32_t ipv4;
|
||||
static u_int32_t adv_ipv4;
|
||||
static struct in6_addr ipv6;
|
||||
static struct in6_addr adv_ipv6;
|
||||
static u_int32_t listenp;
|
||||
static u_int16_t listenport;
|
||||
static struct in6_addr udp_listenp;
|
||||
static u_int16_t udp_listenport;
|
||||
static int tos;
|
||||
static int table;
|
||||
static int no_fallback;
|
||||
static int timeout;
|
||||
static int silent_timeout;
|
||||
static int port_min;
|
||||
static int port_max;
|
||||
static u_int32_t redis_ip;
|
||||
static u_int16_t redis_port;
|
||||
static int redis_db = -1;
|
||||
static char *b2b_url;
|
||||
|
||||
|
||||
|
||||
|
||||
static void signals(void) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
}
|
||||
|
||||
static int rlim(int res, rlim_t val) {
|
||||
struct rlimit rlim;
|
||||
|
||||
ZERO(rlim);
|
||||
rlim.rlim_cur = rlim.rlim_max = val;
|
||||
return setrlimit(res, &rlim);
|
||||
}
|
||||
|
||||
static void resources(void) {
|
||||
int tryv;
|
||||
|
||||
rlim(RLIMIT_CORE, RLIM_INFINITY);
|
||||
for (tryv = ((1<<16) - 1); tryv && rlim(RLIMIT_NOFILE, tryv) == -1; tryv >>= 1)
|
||||
;
|
||||
|
||||
rlim(RLIMIT_DATA, RLIM_INFINITY);
|
||||
rlim(RLIMIT_RSS, RLIM_INFINITY);
|
||||
rlim(RLIMIT_AS, RLIM_INFINITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int parse_ip_port(u_int32_t *ip, u_int16_t *port, char *s) {
|
||||
char *p = NULL;
|
||||
int ret = -1;
|
||||
|
||||
p = strchr(s, ':');
|
||||
if (p) {
|
||||
*p++ = 0;
|
||||
*ip = inet_addr(s);
|
||||
if (*ip == -1)
|
||||
goto out;
|
||||
*port = atoi(p);
|
||||
}
|
||||
else {
|
||||
*ip = 0;
|
||||
if (strchr(s, '.'))
|
||||
goto out;
|
||||
*port = atoi(s);
|
||||
}
|
||||
if (!*port)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (p)
|
||||
*--p = ':';
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_ip6_port(struct in6_addr *ip6, u_int16_t *port, char *s) {
|
||||
u_int32_t ip;
|
||||
char *p;
|
||||
|
||||
if (!parse_ip_port(&ip, port, s)) {
|
||||
if (ip)
|
||||
in4_to_6(ip6, ip);
|
||||
else
|
||||
*ip6 = in6addr_any;
|
||||
return 0;
|
||||
}
|
||||
if (*s != '[')
|
||||
return -1;
|
||||
p = strstr(s, "]:");
|
||||
if (!p)
|
||||
return -1;
|
||||
*p = '\0';
|
||||
if (inet_pton(AF_INET6, s+1, ip6) != 1)
|
||||
goto fail;
|
||||
*p = ']';
|
||||
*port = atoi(p+2);
|
||||
if (!*port)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*p = ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void options(int *argc, char ***argv) {
|
||||
static char *ipv4s;
|
||||
static char *adv_ipv4s;
|
||||
static char *ipv6s;
|
||||
static char *adv_ipv6s;
|
||||
static char *listenps;
|
||||
static char *listenudps;
|
||||
static char *redisps;
|
||||
static int version;
|
||||
|
||||
static GOptionEntry e[] = {
|
||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL },
|
||||
{ "table", 't', 0, G_OPTION_ARG_INT, &table, "Kernel table to use", "INT" },
|
||||
{ "no-fallback",'F', 0, G_OPTION_ARG_NONE, &no_fallback, "Only start when kernel module is available", NULL },
|
||||
{ "ip", 'i', 0, G_OPTION_ARG_STRING, &ipv4s, "Local IPv4 address for RTP", "IP" },
|
||||
{ "advertised-ip", 'a', 0, G_OPTION_ARG_STRING, &adv_ipv4s, "IPv4 address to advertise", "IP" },
|
||||
{ "ip6", 'I', 0, G_OPTION_ARG_STRING, &ipv6s, "Local IPv6 address for RTP", "IP6" },
|
||||
{ "advertised-ip6",'A',0,G_OPTION_ARG_STRING, &adv_ipv6s, "IPv6 address to advertise", "IP6" },
|
||||
{ "listen", 'l', 0, G_OPTION_ARG_STRING, &listenps, "TCP port to listen on", "[IP:]PORT" },
|
||||
{ "listen-udp", 'u', 0, G_OPTION_ARG_STRING, &listenudps, "UDP port to listen on", "[IP46:]PORT" },
|
||||
{ "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "TOS value to set on streams", "INT" },
|
||||
{ "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" },
|
||||
{ "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" },
|
||||
{ "pidfile", 'p', 0, G_OPTION_ARG_STRING, &pidfile, "Write PID to file", "FILE" },
|
||||
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Don't fork to background", NULL },
|
||||
{ "port-min", 'm', 0, G_OPTION_ARG_INT, &port_min, "Lowest port to use for RTP", "INT" },
|
||||
{ "port-max", 'M', 0, G_OPTION_ARG_INT, &port_max, "Highest port to use for RTP", "INT" },
|
||||
{ "redis", 'r', 0, G_OPTION_ARG_STRING, &redisps, "Connect to Redis database", "IP:PORT" },
|
||||
{ "redis-db", 'R', 0, G_OPTION_ARG_INT, &redis_db, "Which Redis DB to use", "INT" },
|
||||
{ "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &b2b_url, "XMLRPC URL of B2B UA" , "STRING" },
|
||||
{ NULL, }
|
||||
};
|
||||
|
||||
GOptionContext *c;
|
||||
GError *er = NULL;
|
||||
|
||||
c = g_option_context_new(" - next-generation media proxy");
|
||||
g_option_context_add_main_entries(c, e, NULL);
|
||||
if (!g_option_context_parse(c, argc, argv, &er))
|
||||
die("Bad command line: %s\n", er->message);
|
||||
|
||||
if (version)
|
||||
die("%s\n", MEDIAPROXY_VERSION);
|
||||
|
||||
if (!ipv4s)
|
||||
die("Missing option --ip\n");
|
||||
if (!listenps && !listenudps)
|
||||
die("Missing option --listen or --listen-udp\n");
|
||||
|
||||
ipv4 = inet_addr(ipv4s);
|
||||
if (ipv4 == -1)
|
||||
die("Invalid IPv4 address (--ip)\n");
|
||||
|
||||
if (adv_ipv4s) {
|
||||
adv_ipv4 = inet_addr(adv_ipv4s);
|
||||
if (adv_ipv4 == -1)
|
||||
die("Invalid IPv4 address (--advertised-ip)\n");
|
||||
}
|
||||
|
||||
if (ipv6s) {
|
||||
if (smart_pton(AF_INET6, ipv6s, &ipv6) != 1)
|
||||
die("Invalid IPv6 address (--ip6)\n");
|
||||
}
|
||||
if (adv_ipv6s) {
|
||||
if (smart_pton(AF_INET6, adv_ipv6s, &adv_ipv6) != 1)
|
||||
die("Invalid IPv6 address (--advertised-ip6)\n");
|
||||
}
|
||||
|
||||
if (listenps) {
|
||||
if (parse_ip_port(&listenp, &listenport, listenps))
|
||||
die("Invalid IP or port (--listen)\n");
|
||||
}
|
||||
if (listenudps) {
|
||||
if (parse_ip6_port(&udp_listenp, &udp_listenport, listenudps))
|
||||
die("Invalid IP or port (--listen-udp)\n");
|
||||
}
|
||||
|
||||
if (tos < 0 || tos > 255)
|
||||
die("Invalid TOS value\n");
|
||||
|
||||
if (timeout <= 0)
|
||||
timeout = 60;
|
||||
if (silent_timeout <= 0)
|
||||
silent_timeout = 3600;
|
||||
|
||||
if (redisps) {
|
||||
if (parse_ip_port(&redis_ip, &redis_port, redisps) || !redis_ip)
|
||||
die("Invalid IP or port (--redis)\n");
|
||||
if (redis_db < 0)
|
||||
die("Must specify Redis DB number (--redis-db) when using Redis\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void daemonize(void) {
|
||||
printf("Going to background...\n");
|
||||
if (fork())
|
||||
_exit(0);
|
||||
freopen("/dev/null", "r", stdin);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
setpgrp();
|
||||
}
|
||||
|
||||
static void wpidfile(void) {
|
||||
FILE *fp;
|
||||
|
||||
if (!pidfile)
|
||||
return;
|
||||
|
||||
fp = fopen(pidfile, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "%u\n", getpid());
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct poller *p;
|
||||
struct callmaster *m;
|
||||
struct control *c;
|
||||
struct control_udp *cu;
|
||||
int kfd = -1;
|
||||
int ret;
|
||||
void *dlh;
|
||||
const char **strp;
|
||||
|
||||
options(&argc, &argv);
|
||||
signals();
|
||||
resources();
|
||||
|
||||
|
||||
if (table >= 0 && kernel_create_table(table)) {
|
||||
fprintf(stderr, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
|
||||
mylog(LOG_CRIT, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
|
||||
table = -1;
|
||||
if (no_fallback)
|
||||
exit(-1);
|
||||
}
|
||||
if (table >= 0) {
|
||||
kfd = kernel_open_table(table);
|
||||
if (kfd == -1) {
|
||||
fprintf(stderr, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
|
||||
mylog(LOG_CRIT, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
|
||||
table = -1;
|
||||
if (no_fallback)
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
p = poller_new();
|
||||
if (!p)
|
||||
die("poller creation failed\n");
|
||||
|
||||
m = callmaster_new(p);
|
||||
if (!m)
|
||||
return -1;
|
||||
m->kernelfd = kfd;
|
||||
m->kernelid = table;
|
||||
m->ipv4 = ipv4;
|
||||
m->adv_ipv4 = adv_ipv4;
|
||||
m->ipv6 = ipv6;
|
||||
m->adv_ipv6 = adv_ipv6;
|
||||
m->port_min = port_min;
|
||||
m->port_max = port_max;
|
||||
m->timeout = timeout;
|
||||
m->silent_timeout = silent_timeout;
|
||||
m->tos = tos;
|
||||
m->b2b_url = b2b_url;
|
||||
|
||||
c = NULL;
|
||||
if (listenport) {
|
||||
c = control_new(p, listenp, listenport, m);
|
||||
if (!c)
|
||||
die("Failed to open TCP control connection port\n");
|
||||
}
|
||||
|
||||
cu = NULL;
|
||||
if (udp_listenport) {
|
||||
cu = control_udp_new(p, udp_listenp, udp_listenport, m);
|
||||
if (!cu)
|
||||
die("Failed to open UDP control connection port\n");
|
||||
}
|
||||
|
||||
if (redis_ip) {
|
||||
dlh = dlopen(MP_PLUGIN_DIR "/redis.so", RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!dlh)
|
||||
die("Failed to open redis plugin, aborting (%s)\n", dlerror());
|
||||
strp = dlsym(dlh, "__module_version");
|
||||
if (!strp || !*strp || strcmp(*strp, "redis/1.0.0"))
|
||||
die("Incorrect redis module version: %s\n", *strp);
|
||||
|
||||
dlresolve(dlh, redis_new);
|
||||
dlresolve(dlh, redis_restore);
|
||||
dlresolve(dlh, redis_update);
|
||||
dlresolve(dlh, redis_delete);
|
||||
dlresolve(dlh, redis_wipe);
|
||||
|
||||
m->redis = redis_new(redis_ip, redis_port, redis_db);
|
||||
if (!m->redis)
|
||||
die("Cannot start up without Redis database\n");
|
||||
}
|
||||
|
||||
mylog(LOG_INFO, "Startup complete");
|
||||
|
||||
if (!foreground)
|
||||
daemonize();
|
||||
wpidfile();
|
||||
|
||||
if (m->redis) {
|
||||
if (redis_restore(m))
|
||||
die("Refusing to continue without working Redis database\n");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ret = poller_poll(p, 100);
|
||||
if (ret == -1)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "poller.h"
|
||||
|
||||
|
||||
void dummy(int a, void *b) {
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
struct poller *p;
|
||||
struct poller_item i;
|
||||
|
||||
p = poller_new();
|
||||
if (!p) {
|
||||
fprintf(stderr, "poller creation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(p->items_size == 0);
|
||||
assert(p->pollfds_size == 0);
|
||||
|
||||
i.readable = dummy;
|
||||
i.writeable = dummy;
|
||||
i.closed = dummy;
|
||||
|
||||
|
||||
i.fd = 3;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 4;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 2;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 6;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 0;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 1;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 5;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 7;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
i.fd = 9;
|
||||
assert(poller_add_item(p, &i) == 0);
|
||||
|
||||
|
||||
assert(poller_del_item(p, 10) == -1);
|
||||
assert(poller_del_item(p, 6) == 0);
|
||||
assert(poller_del_item(p, 8) == -1);
|
||||
assert(poller_del_item(p, 0) == 0);
|
||||
assert(poller_del_item(p, 3) == 0);
|
||||
assert(poller_del_item(p, 11) == -1);
|
||||
assert(poller_del_item(p, 9) == 0);
|
||||
assert(poller_del_item(p, 11) == -1);
|
||||
assert(poller_del_item(p, 4) == 0);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define POLLER_BSEARCH(b,l,k,e) mybsearch(b, l, sizeof(struct pollfd), k, OFFSET_OF(struct pollfd, fd), sizeof(*(k)), e)
|
||||
|
||||
|
||||
|
||||
|
||||
struct timer_item {
|
||||
void (*func)(void *);
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct poller *poller_new(void) {
|
||||
struct poller *p;
|
||||
|
||||
p = malloc(sizeof(*p));
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->now = time(NULL);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
int poller_add_item(struct poller *p, struct poller_item *i) {
|
||||
struct poller_item *ip;
|
||||
struct pollfd *pf;
|
||||
int idx;
|
||||
unsigned int u;
|
||||
|
||||
if (!p || !i)
|
||||
return -1;
|
||||
if (i->fd < 0)
|
||||
return -1;
|
||||
if (!i->readable && !i->writeable)
|
||||
return -1;
|
||||
if (!i->closed)
|
||||
return -1;
|
||||
|
||||
if (i->fd < p->items_size && p->items[i->fd])
|
||||
return -1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &i->fd, 0);
|
||||
assert(idx < 0);
|
||||
|
||||
idx *= -1;
|
||||
idx--;
|
||||
|
||||
p->pollfds_size++;
|
||||
p->pollfds = realloc(p->pollfds, p->pollfds_size * sizeof(*p->pollfds));
|
||||
memmove(p->pollfds + idx + 1, p->pollfds + idx, (p->pollfds_size - idx - 1) * sizeof(*p->pollfds));
|
||||
pf = &p->pollfds[idx];
|
||||
|
||||
pf->fd = i->fd;
|
||||
pf->events = POLLHUP | POLLERR | ((i->writeable && i->blocked) ? POLLOUT : 0) | (i->readable ? POLLIN : 0);
|
||||
pf->revents = 0;
|
||||
|
||||
if (i->fd >= p->items_size) {
|
||||
u = p->items_size;
|
||||
p->items_size = i->fd + 1;
|
||||
p->items = realloc(p->items, sizeof(*p->items) * p->items_size);
|
||||
memset(p->items + u, 0, sizeof(*p->items) * (p->items_size - u - 1));
|
||||
}
|
||||
|
||||
ip = malloc(sizeof(*ip));
|
||||
memcpy(ip, i, sizeof(*ip));
|
||||
p->items[i->fd] = ip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_del_item(struct poller *p, int fd) {
|
||||
int idx;
|
||||
|
||||
if (!p || fd < 0)
|
||||
return -1;
|
||||
if (fd >= p->items_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items[fd])
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
memmove(p->pollfds + idx, p->pollfds + idx + 1, (p->pollfds_size - idx - 1) * sizeof(*p->pollfds));
|
||||
p->pollfds_size--;
|
||||
p->pollfds = realloc(p->pollfds, p->pollfds_size * sizeof(*p->pollfds));
|
||||
|
||||
if (p->pollfds_work) {
|
||||
idx = POLLER_BSEARCH(p->pollfds_work, p->pollfds_work_size, &fd, 1);
|
||||
|
||||
if (idx != -1)
|
||||
p->pollfds_work[idx].fd = -1;
|
||||
}
|
||||
|
||||
free(p->items[fd]);
|
||||
p->items[fd] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_update_item(struct poller *p, struct poller_item *i) {
|
||||
struct poller_item *np;
|
||||
|
||||
if (!p || !i)
|
||||
return -1;
|
||||
if (i->fd < 0)
|
||||
return -1;
|
||||
if (!i->readable && !i->writeable)
|
||||
return -1;
|
||||
if (!i->closed)
|
||||
return -1;
|
||||
|
||||
if (i->fd >= p->items_size || !(np = p->items[i->fd]))
|
||||
return poller_add_item(p, i);
|
||||
|
||||
np->ptr = i->ptr;
|
||||
np->readable = i->readable;
|
||||
np->writeable = i->writeable;
|
||||
np->closed = i->closed;
|
||||
np->timer = i->timer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int poller_poll(struct poller *p, int timeout) {
|
||||
struct pollfd *pfd, *pf;
|
||||
int ret, i;
|
||||
struct poller_item *it;
|
||||
int idx;
|
||||
time_t last;
|
||||
int do_timer;
|
||||
GList *li;
|
||||
struct timer_item *ti;
|
||||
|
||||
if (!p)
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items_size)
|
||||
return -1;
|
||||
|
||||
p->pollfds_work_size = i = p->pollfds_size;
|
||||
p->pollfds_work = pfd = malloc(sizeof(*pfd) * i);
|
||||
memcpy(pfd, p->pollfds, sizeof(*pfd) * i);
|
||||
|
||||
do_timer = 0;
|
||||
last = p->now;
|
||||
p->now = time(NULL);
|
||||
if (last != p->now) {
|
||||
do_timer = 1;
|
||||
ret = i;
|
||||
|
||||
for (li = p->timers; li; li = li->next) {
|
||||
ti = li->data;
|
||||
ti->func(ti->ptr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = poll(pfd, i, timeout);
|
||||
if (errno == EINTR)
|
||||
ret = 0;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
pf = pfd;
|
||||
for (pf = pfd; i; pf++) {
|
||||
i--;
|
||||
|
||||
if (pf->fd < 0)
|
||||
continue;
|
||||
|
||||
it = (pf->fd < p->items_size) ? p->items[pf->fd] : NULL;
|
||||
if (!it)
|
||||
continue;
|
||||
|
||||
if (do_timer) {
|
||||
if (it->timer)
|
||||
it->timer(it->fd, it->ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it->error) {
|
||||
it->closed(it->fd, it->ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((pf->revents & (POLLERR | POLLHUP)))
|
||||
it->closed(it->fd, it->ptr);
|
||||
else if ((pf->revents & POLLOUT)) {
|
||||
it->blocked = 0;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &it->fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
p->pollfds[idx].events &= ~POLLOUT;
|
||||
|
||||
it->writeable(it->fd, it->ptr);
|
||||
}
|
||||
else if ((pf->revents & POLLIN))
|
||||
it->readable(it->fd, it->ptr);
|
||||
else if (!pf->revents)
|
||||
continue;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
free(pfd);
|
||||
p->pollfds_work = NULL;
|
||||
p->pollfds_work_size = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void poller_blocked(struct poller *p, int fd) {
|
||||
int idx;
|
||||
|
||||
if (!p || fd < 0)
|
||||
return;
|
||||
if (fd >= p->items_size)
|
||||
return;
|
||||
if (!p->items || !p->items[fd])
|
||||
return;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return;
|
||||
if (!p->items[fd]->writeable)
|
||||
return;
|
||||
|
||||
p->items[fd]->blocked = 1;
|
||||
|
||||
idx = POLLER_BSEARCH(p->pollfds, p->pollfds_size, &fd, 1);
|
||||
assert(idx != -1);
|
||||
|
||||
p->pollfds[idx].events |= POLLOUT;
|
||||
}
|
||||
|
||||
void poller_error(struct poller *p, int fd) {
|
||||
if (!p || fd < 0)
|
||||
return;
|
||||
if (fd >= p->items_size)
|
||||
return;
|
||||
if (!p->items || !p->items[fd])
|
||||
return;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return;
|
||||
if (!p->items[fd]->writeable)
|
||||
return;
|
||||
|
||||
p->items[fd]->error = 1;
|
||||
p->items[fd]->blocked = 1;
|
||||
}
|
||||
|
||||
int poller_isblocked(struct poller *p, int fd) {
|
||||
if (!p || fd < 0)
|
||||
return -1;
|
||||
if (fd >= p->items_size)
|
||||
return -1;
|
||||
if (!p->items || !p->items[fd])
|
||||
return -1;
|
||||
if (!p->pollfds || !p->pollfds_size)
|
||||
return -1;
|
||||
if (!p->items[fd]->writeable)
|
||||
return -1;
|
||||
|
||||
return p->items[fd]->blocked;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int poller_timer(struct poller *p, void (*f)(void *), void *ptr) {
|
||||
struct timer_item *i;
|
||||
|
||||
if (!p || !f)
|
||||
return -1;
|
||||
|
||||
i = malloc(sizeof(*i));
|
||||
ZERO(*i);
|
||||
|
||||
i->func = f;
|
||||
i->ptr = ptr;
|
||||
|
||||
p->timers = g_list_prepend(p->timers, i);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#ifndef __POLLER_H__
|
||||
#define __POLLER_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
struct poller_item {
|
||||
int fd;
|
||||
void *ptr;
|
||||
|
||||
void (*readable)(int, void *);
|
||||
void (*writeable)(int, void *);
|
||||
void (*closed)(int, void *);
|
||||
void (*timer)(int, void *);
|
||||
|
||||
int blocked:1;
|
||||
int error:1;
|
||||
};
|
||||
|
||||
struct poller {
|
||||
struct poller_item **items;
|
||||
unsigned int items_size;
|
||||
struct pollfd *pollfds;
|
||||
unsigned int pollfds_size;
|
||||
GList *timers;
|
||||
|
||||
time_t now;
|
||||
|
||||
struct pollfd *pollfds_work;
|
||||
unsigned int pollfds_work_size;
|
||||
};
|
||||
|
||||
|
||||
struct poller *poller_new(void);
|
||||
int poller_add_item(struct poller *, struct poller_item *);
|
||||
int poller_update_item(struct poller *, struct poller_item *);
|
||||
int poller_del_item(struct poller *, int);
|
||||
int poller_poll(struct poller *, int);
|
||||
void poller_blocked(struct poller *, int);
|
||||
int poller_isblocked(struct poller *, int);
|
||||
void poller_error(struct poller *, int);
|
||||
|
||||
int poller_timer(struct poller *, void (*)(void *), void *);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,7 @@
|
||||
#include "redis.h"
|
||||
|
||||
struct redis *(*redis_new)(u_int32_t, u_int16_t, int);
|
||||
int (*redis_restore)(struct callmaster *);
|
||||
void (*redis_update)(struct call *);
|
||||
void (*redis_delete)(struct call *);
|
||||
void (*redis_wipe)(struct callmaster *);
|
@ -0,0 +1,26 @@
|
||||
#ifndef __REDIS_H__
|
||||
#define __REDIS_H__
|
||||
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
|
||||
|
||||
struct callmaster;
|
||||
struct call;
|
||||
|
||||
|
||||
|
||||
extern struct redis *(*redis_new)(u_int32_t, u_int16_t, int);
|
||||
extern int (*redis_restore)(struct callmaster *);
|
||||
extern void (*redis_update)(struct call *);
|
||||
extern void (*redis_delete)(struct call *);
|
||||
extern void (*redis_wipe)(struct callmaster *);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "streambuf.h"
|
||||
#include "poller.h"
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
|
||||
struct streambuf *streambuf_new(struct poller *p, int fd) {
|
||||
struct streambuf *b;
|
||||
|
||||
b = malloc(sizeof(*b));
|
||||
ZERO(*b);
|
||||
|
||||
b->buf = g_string_new("");
|
||||
b->fd = fd;
|
||||
b->poller = p;
|
||||
b->active = p->now;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void streambuf_destroy(struct streambuf *b) {
|
||||
g_string_free(b->buf, TRUE);
|
||||
free(b);
|
||||
}
|
||||
|
||||
|
||||
int streambuf_writeable(struct streambuf *b) {
|
||||
int ret;
|
||||
unsigned int out;
|
||||
|
||||
for (;;) {
|
||||
if (!b->buf->len)
|
||||
break;
|
||||
|
||||
out = (b->buf->len > 1024) ? 1024 : b->buf->len;
|
||||
ret = write(b->fd, b->buf->str, out);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
return -1;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
g_string_erase(b->buf, 0, ret);
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
if (ret != out) {
|
||||
poller_blocked(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int streambuf_readable(struct streambuf *b) {
|
||||
int ret;
|
||||
char buf[1024];
|
||||
|
||||
for (;;) {
|
||||
ret = read(b->fd, buf, 1024);
|
||||
|
||||
if (ret == 0)
|
||||
return -1;
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_string_append_len(b->buf, buf, ret);
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *streambuf_getline(struct streambuf *b) {
|
||||
char *p;
|
||||
int len;
|
||||
char *s = NULL;
|
||||
|
||||
for (;;) {
|
||||
if (s)
|
||||
free(s);
|
||||
|
||||
p = memchr(b->buf->str, '\n', b->buf->len);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
len = p - b->buf->str;
|
||||
if (len == 0) {
|
||||
g_string_erase(b->buf, 0, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
s = malloc(len + 1);
|
||||
memcpy(s, b->buf->str, len);
|
||||
s[len] = '\0';
|
||||
g_string_erase(b->buf, 0, len + 1);
|
||||
|
||||
if (s[--len] == '\r') {
|
||||
if (len == 0)
|
||||
continue;
|
||||
s[len] = '\0';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned int streambuf_bufsize(struct streambuf *b) {
|
||||
return b->buf->len;
|
||||
}
|
||||
|
||||
|
||||
void streambuf_printf(struct streambuf *b, char *f, ...) {
|
||||
va_list va;
|
||||
GString *gs;
|
||||
|
||||
va_start(va, f);
|
||||
gs = g_string_new("");
|
||||
g_string_vprintf(gs, f, va);
|
||||
va_end(va);
|
||||
|
||||
streambuf_write(b, gs->str, gs->len);
|
||||
g_string_free(gs, TRUE);
|
||||
}
|
||||
|
||||
void streambuf_write(struct streambuf *b, char *s, unsigned int len) {
|
||||
unsigned int out;
|
||||
int ret;
|
||||
|
||||
while (len && !poller_isblocked(b->poller, b->fd)) {
|
||||
out = (len > 1024) ? 1024 : len;
|
||||
ret = write(b->fd, s, out);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
poller_error(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
poller_blocked(b->poller, b->fd);
|
||||
break;
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
s += ret;
|
||||
len -= ret;
|
||||
b->active = b->poller->now;
|
||||
}
|
||||
|
||||
if (b->buf->len > 5242880)
|
||||
poller_error(b->poller, b->fd);
|
||||
else if (len)
|
||||
g_string_append_len(b->buf, s, len);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef __BUFFER_H__
|
||||
#define __BUFFER_H__
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
|
||||
struct poller;
|
||||
|
||||
|
||||
|
||||
struct streambuf {
|
||||
GString *buf;
|
||||
int fd;
|
||||
struct poller *poller;
|
||||
time_t active;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct streambuf *streambuf_new(struct poller *, int);
|
||||
void streambuf_destroy(struct streambuf *);
|
||||
int streambuf_writeable(struct streambuf *);
|
||||
int streambuf_readable(struct streambuf *);
|
||||
char *streambuf_getline(struct streambuf *);
|
||||
unsigned int streambuf_bufsize(struct streambuf *);
|
||||
void streambuf_printf(struct streambuf *, char *, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
void streambuf_write(struct streambuf *, char *, unsigned int);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
ngcp-mediaproxy-ng-kernel for Debian
|
||||
------------------------------------
|
||||
|
||||
This package only works with kernels >= 2.6.32.
|
||||
|
||||
Instructions for usage
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1) module-assistant:
|
||||
|
||||
% sudo apt-get install linux-headers-$(uname -r)
|
||||
% sudo apt-get install ngcp-mediaproxy-ng-kernel-source
|
||||
% sudo m-a prepare
|
||||
% sudo m-a -v -f -l $(uname -r) -k /usr/src/linux-headers-$(uname -r) a-b ngcp-mediaproxy-ng-kernel
|
||||
|
||||
How to debug build process:
|
||||
|
||||
% sudo m-a -d -v --text-mode -l $(uname -r) -k /usr/src/linux-headers-$(uname -r) a-i ngcp-mediaproxy-ng-kernel
|
||||
|
||||
2) DKMS:
|
||||
|
||||
% sudo apt-get install ngcp-mediaproxy-ng-kernel-dkms
|
||||
% sudo dkms add -m ngcp-mediaproxy-ng -v 0.1
|
||||
% sudo dkms build --kernelsourcedir /usr/src/linux-headers-$(uname -r) -m ngcp-mediaproxy-ng -v 0.1
|
||||
% sudo dkms install -m ngcp-mediaproxy-ng -v 0.1
|
@ -0,0 +1,138 @@
|
||||
ngcp-mediaproxy-ng (1.6.6) unstable; urgency=low
|
||||
|
||||
* Support userspace-only operation mode
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Wed, 16 May 2012 09:33:18 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.5) unstable; urgency=low
|
||||
|
||||
* Fix daemon failing to correctly interpret "delete full call" message
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Fri, 04 May 2012 11:21:11 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.4) unstable; urgency=low
|
||||
|
||||
* Fix segfault
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 06 Mar 2012 09:51:07 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.3) unstable; urgency=low
|
||||
|
||||
* Fix the UDP control protocol
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 06 Mar 2012 07:12:47 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.2) unstable; urgency=low
|
||||
|
||||
* Check from/to tags in delete message
|
||||
* Implement via-branch handling
|
||||
* Don't strip debug symbols
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Mon, 05 Mar 2012 04:31:07 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.1) unstable; urgency=low
|
||||
|
||||
* Correctly remember address family across re-invites etc
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Wed, 08 Feb 2012 10:34:33 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.6.0) unstable; urgency=low
|
||||
|
||||
* Add full IPv6 support
|
||||
* Attempt to handle unparsable proxy commands as much as possible
|
||||
* Improve human-readable output in /proc
|
||||
* Fix handling of calls with multiple media streams
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Thu, 26 Jan 2012 07:30:25 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.5.3) unstable; urgency=low
|
||||
|
||||
* Fix incorrect handling of lookups received without prior request
|
||||
* Fix a long-standing bug that caused from/to tags to be ignored when using UDP protocol
|
||||
* Properly timeout and cleanup UDP cookies
|
||||
* Fix table 0 not showing up in /proc/mediaproxy/list
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Thu, 08 Dec 2011 11:05:30 -0500
|
||||
|
||||
ngcp-mediaproxy-ng (1.5.2) unstable; urgency=low
|
||||
|
||||
* Fix bad tagging
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 20 Sep 2011 10:41:42 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.5.1) unstable; urgency=low
|
||||
|
||||
* Return a dummy/error reply over UDP when a call doesn't exist, so
|
||||
kamailio doesn't think we're dead
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 20 Sep 2011 10:41:42 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.5.0) unstable; urgency=low
|
||||
|
||||
* Rework port re-use logic so it never opens new ports when it doesn't
|
||||
need to.
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Thu, 15 Sep 2011 10:42:57 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.4.2) unstable; urgency=low
|
||||
|
||||
* Slightly increase syslog verbosity
|
||||
* Fix obscure 3-way call connect issue
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Fri, 02 Sep 2011 17:09:38 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.4.1) unstable; urgency=low
|
||||
|
||||
* Fix a memory leak
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Wed, 10 Aug 2011 17:01:56 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.4.0) unstable; urgency=low
|
||||
|
||||
* Support HA through persistent Redis storage
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Fri, 10 Jun 2011 13:50:50 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.5) unstable; urgency=low
|
||||
|
||||
* Fix dst reference count issues causing kernel warnings under some circumstances
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Thu, 19 May 2011 13:43:16 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.4) unstable; urgency=low
|
||||
|
||||
* Make the daemon more aggressive with invalidating peer information
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Thu, 05 May 2011 16:08:31 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.3) unstable; urgency=low
|
||||
|
||||
* Add --advertised-ip to defaults file.
|
||||
|
||||
-- Andreas Granig <agranig@sipwise.com> Wed, 04 May 2011 23:26:30 +0200
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.2) unstable; urgency=low
|
||||
|
||||
* Introduce --advertised-ip parameter
|
||||
* Minor code & help text cleanups
|
||||
|
||||
-- Richard Fuchs <rfuchs@sipwise.com> Tue, 03 May 2011 17:20:11 -0400
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.1) unstable; urgency=low
|
||||
|
||||
* dkms postinst: do not execute init script if it is not present yet.
|
||||
|
||||
-- Michael Prokop <mprokop@sipwise.com> Fri, 29 Apr 2011 17:18:41 +0200
|
||||
|
||||
ngcp-mediaproxy-ng (1.3.0) unstable; urgency=low
|
||||
|
||||
* Release for 2.2
|
||||
* Fixed version number to align with old, non-debianized versioning scheme.
|
||||
|
||||
-- Andreas Granig <agranig@sipwise.com> Fri, 29 Apr 2011 12:01:56 +0200
|
||||
|
||||
ngcp-mediaproxy-ng (0.1) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Andreas Granig <agranig@sipwise.com> Tue, 26 Apr 2011 18:55:01 +0200
|
@ -0,0 +1 @@
|
||||
5
|
@ -0,0 +1,60 @@
|
||||
Source: ngcp-mediaproxy-ng
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Sipwise Development Team <support@sipwise.com>
|
||||
Build-Depends: debhelper (>= 5), iptables-dev (>= 1.4), libglib2.0-dev, libpcre3-dev,
|
||||
libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07), libcurl4-openssl-dev | libcurl4-gnutls-dev |
|
||||
libcurl3-openssl-dev | libcurl3-gnutls-dev
|
||||
Standards-Version: 3.9.2
|
||||
Homepage: http://sipwise.com/
|
||||
|
||||
Package: ngcp-mediaproxy-ng-daemon
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Pre-Depends: ngcp-mediaproxy-ng-kernel-dkms (>= ${source:Version})
|
||||
Description: Proxy for RTP and media streams used in NGCP, userspace part.
|
||||
This daemon handles the first stages of proxying media streams and talks to
|
||||
the kernel part of the proxy for eventual high-performance packet forwarding.
|
||||
|
||||
Package: ngcp-mediaproxy-ng-iptables
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: IPtables extension module for the kernel-space NGCP media proxy.
|
||||
Provides the IPtables extension needed to configure the mediaproxy rule.
|
||||
|
||||
Package: ngcp-mediaproxy-ng
|
||||
Architecture: all
|
||||
Depends: ngcp-mediaproxy-ng-daemon (>= ${source:Version}), ngcp-mediaproxy-ng-iptables (>= ${source:Version}),
|
||||
ngcp-mediaproxy-ng-kernel-dkms (>= ${source:Version})
|
||||
Description: NGCP RTP/media proxy - meta package.
|
||||
This is a meta package for easy installation of all three parts of the NGCP
|
||||
media proxy. It will install the user-space daemon, the kernel-space IPtables
|
||||
module, and the IPtables extension module.
|
||||
|
||||
Package: ngcp-mediaproxy-ng-kernel-source
|
||||
Architecture: all
|
||||
Depends: debhelper (>= 5), module-assistant, ${misc:Depends}
|
||||
Description: IPtables kernel module for the NGCP media proxy - source.
|
||||
Provides the kernel-space part of the NGCP media proxy for high-
|
||||
performance packet forwarding.
|
||||
This package contains the source to be built with module-assistant or
|
||||
kernel-package.
|
||||
|
||||
Package: ngcp-mediaproxy-ng-kernel-dkms
|
||||
Architecture: all
|
||||
Depends: dkms (>= 1.95), ${misc:Depends}
|
||||
Description: IPtables kernel module for the NGCP media proxy - DKMS.
|
||||
Provides the kernel-space part of the NGCP media proxy for high-
|
||||
performance packet forwarding.
|
||||
This package contains the source to be built with dkms.
|
||||
|
||||
Package: ngcp-mediaproxy-ng-dev
|
||||
Architecture: all
|
||||
Section: libdevel
|
||||
Depends: debhelper (>= 5), ${misc:Depends}
|
||||
Description: Development files for mediaproxy-ng
|
||||
This package provides the header files of the mediaproxy-ng
|
||||
software.
|
||||
.
|
||||
Install this package if you wish to develop your own programs using
|
||||
mediaproxy-ng.
|
@ -0,0 +1,15 @@
|
||||
Source: ngcp-mediaproxy-ng-kernel
|
||||
Section: kernel
|
||||
Priority: optional
|
||||
Maintainer: Richard Fuchs <rfuchs@sipwise.com>
|
||||
Build-Depends: debhelper (>= 5)
|
||||
Standards-Version: 3.9.1
|
||||
Homepage: http://sipwise.com/
|
||||
|
||||
Package: ngcp-mediaproxy-ng-kernel-modules-_KVERS_
|
||||
Architecture: any
|
||||
Depends: linux-modules-_KVERS_ | linux-image-_KVERS_
|
||||
Provides: ngcp-mediaproxy-ng-kernel
|
||||
Description: TODO
|
||||
This package provides the ngcp-mediaproxy-ng module for
|
||||
the Linux kernel version _KVERS_.
|
@ -0,0 +1,7 @@
|
||||
Upstream Author: The Sipwise Team - http://sipwise.com/
|
||||
Copyright: Copyright (c) 2007-2011 Sipwise GmbH, Austria
|
||||
License: All software included in this package is
|
||||
Copyright (c) Sipwise GmbH, Austria.
|
||||
All rights reserved. You may not copy, distribute
|
||||
or modify without prior written permission from
|
||||
Sipwise GmbH, Austria.
|
@ -0,0 +1 @@
|
||||
usr/sbin
|
@ -0,0 +1,7 @@
|
||||
PACKAGE_NAME="ngcp-mediaproxy-ng"
|
||||
PACKAGE_VERSION="__VERSION__"
|
||||
MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build MEDIAPROXY_VERSION=\"__VERSION__\""
|
||||
CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
|
||||
AUTOINSTALL=yes
|
||||
BUILT_MODULE_NAME[0]="xt_MEDIAPROXY"
|
||||
DEST_MODULE_LOCATION[0]=/extra
|
@ -0,0 +1,19 @@
|
||||
RUN_MEDIAPROXY=no
|
||||
LISTEN=25060
|
||||
LISTEN_UDP=12222
|
||||
# ADDRESS=...
|
||||
# ADV_ADDRESS=...
|
||||
# ADDRESS_IPV6=...
|
||||
# ADV_ADDRESS_IPV6=...
|
||||
TIMEOUT=60
|
||||
SILENT_TIMEOUT=3600
|
||||
PIDFILE=/var/run/ngcp-mediaproxy-ng-daemon.pid
|
||||
FORK=yes
|
||||
# TOS=184
|
||||
TABLE=0
|
||||
# NO_FALLBACK=yes
|
||||
# PORT_MIN=30000
|
||||
# PORT_MAX=50000
|
||||
# REDIS=127.0.0.1:6379
|
||||
# REDIS_DB=1
|
||||
# B2B_URL=http://127.0.0.1:8090/
|
@ -0,0 +1,138 @@
|
||||
#! /bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: mediaproxy-ng
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Ngcp Mediaproxy-ng
|
||||
# Description: Proxy for RTP and other media streams
|
||||
### END INIT INFO
|
||||
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
NAME=ngcp-mediaproxy-ng-daemon
|
||||
DESC="RTP/media proxy"
|
||||
RUN_MEDIAPROXY=no
|
||||
TABLE=0
|
||||
|
||||
case $(dirname $0) in
|
||||
/*) FULLPATH=$(dirname $0);;
|
||||
*) FULLPATH=$(pwd)/$(dirname $0);;
|
||||
esac
|
||||
|
||||
DAEMON=`which mediaproxy-ng`
|
||||
DEFAULTS=/etc/default/${NAME}
|
||||
|
||||
test -f $DAEMON || exit 0
|
||||
|
||||
# Load startup options if available
|
||||
if [ -f $DEFAULTS ]; then
|
||||
. $DEFAULTS || true
|
||||
fi
|
||||
|
||||
if [ "$RUN_MEDIAPROXY" != "yes" ]; then
|
||||
echo "mediaproxy-ng not yet configured. Edit $DEFAULTS first."
|
||||
exit 0
|
||||
fi
|
||||
[ -z "$PIDFILE" ] && PIDFILE="/var/run/mediaproxy-ng.pid"
|
||||
|
||||
set -e
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
OPTIONS=""
|
||||
|
||||
[ -z "$ADDRESS" ] || OPTIONS="$OPTIONS --ip=$ADDRESS"
|
||||
[ -z "$LISTEN" ] || OPTIONS="$OPTIONS --listen=$LISTEN"
|
||||
[ -z "$LISTEN_UDP" ] || OPTIONS="$OPTIONS --listen-udp=$LISTEN_UDP"
|
||||
[ -z "$TIMEOUT" ] || OPTIONS="$OPTIONS --timeout=$TIMEOUT"
|
||||
[ -z "$SILENT_TIMEOUT" ] || OPTIONS="$OPTIONS --silent-timeout=$SILENT_TIMEOUT"
|
||||
[ -z "$PIDFILE" ] || OPTIONS="$OPTIONS --pidfile=$PIDFILE"
|
||||
[ -z "$TOS" ] || OPTIONS="$OPTIONS --tos=$TOS"
|
||||
[ -z "$PORT_MIN" ] || OPTIONS="$OPTIONS --port-min=$PORT_MIN"
|
||||
[ -z "$PORT_MAX" ] || OPTIONS="$OPTIONS --port-max=$PORT_MAX"
|
||||
[ -z "$ADV_ADDRESS" ] || OPTIONS="$OPTIONS --advertised-ip=$ADV_ADDRESS"
|
||||
[ -z "$ADDRESS_IPV6" ] || OPTIONS="$OPTIONS --ip6=$ADDRESS_IPV6"
|
||||
[ -z "$ADV_ADDRESS_IPV6" ] || OPTIONS="$OPTIONS --advertised-ip6=$ADV_ADDRESS_IPV6"
|
||||
[ -z "$REDIS" ] || OPTIONS="$OPTIONS --redis=$REDIS"
|
||||
[ -z "$REDIS_DB" ] || OPTIONS="$OPTIONS --redis-db=$REDIS_DB"
|
||||
[ -z "$B2B_URL" ] || OPTIONS="$OPTIONS --b2b-url=$B2B_URL"
|
||||
[ -z "$NO_FALLBACK" -o \( "$NO_FALLBACK" != "1" -a "$NO_FALLBACK" != "yes" \) ] || OPTIONS="$OPTIONS --no-fallback"
|
||||
OPTIONS="$OPTIONS --table=$TABLE"
|
||||
if test "$FORK" = "no" ; then
|
||||
OPTIONS="$OPTIONS --foreground"
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
|
||||
echo -n "Starting $DESC: $NAME"
|
||||
set +e
|
||||
modprobe xt_MEDIAPROXY
|
||||
echo "del $TABLE" > /proc/mediaproxy/control 2>/dev/null
|
||||
iptables -D INPUT -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
iptables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
ip6tables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
iptables -I INPUT -p udp -j MEDIAPROXY --id $TABLE
|
||||
ip6tables -I INPUT -p udp -j MEDIAPROXY --id $TABLE
|
||||
set -e
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE \
|
||||
--exec $DAEMON -- $OPTIONS || echo -n " already running"
|
||||
log_end_msg $?
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $DESC: $NAME"
|
||||
start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \
|
||||
--exec $DAEMON
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return $?
|
||||
fi
|
||||
set +e
|
||||
echo "del $TABLE" > /proc/mediaproxy/control 2>/dev/null
|
||||
iptables -D INPUT -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
iptables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
ip6tables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
rmmod ipt_MEDIAPROXY 2>/dev/null
|
||||
rmmod xt_MEDIAPROXY 2>/dev/null
|
||||
set -e
|
||||
rm -f $PIDFILE
|
||||
log_end_msg $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
|
||||
echo -n "Restarting $DESC: $NAME"
|
||||
start-stop-daemon --oknodo --stop --quiet --pidfile \
|
||||
$PIDFILE --exec $DAEMON
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return $?
|
||||
fi
|
||||
rm -f $PIDFILE
|
||||
sleep 1
|
||||
set +e
|
||||
if [ -e /proc/mediaproxy/control ]; then
|
||||
echo "del $TABLE" > /proc/mediaproxy/control 2>/dev/null
|
||||
fi
|
||||
iptables -D INPUT -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
iptables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
ip6tables -D INPUT -p udp -j MEDIAPROXY --id $TABLE 2>/dev/null
|
||||
rmmod ipt_MEDIAPROXY 2>/dev/null
|
||||
rmmod xt_MEDIAPROXY 2>/dev/null
|
||||
modprobe xt_MEDIAPROXY
|
||||
iptables -I INPUT -p udp -j MEDIAPROXY --id $TABLE
|
||||
ip6tables -I INPUT -p udp -j MEDIAPROXY --id $TABLE
|
||||
set -e
|
||||
start-stop-daemon --start --quiet --pidfile \
|
||||
$PIDFILE --exec $DAEMON -- $OPTIONS
|
||||
log_end_msg $?
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
@ -0,0 +1 @@
|
||||
daemon/mediaproxy-ng /usr/sbin/
|
@ -0,0 +1,54 @@
|
||||
#!/bin/sh
|
||||
# postinst script for ngcp-mediaproxy-ng-daemon
|
||||
|
||||
set -e
|
||||
|
||||
restart_handler() {
|
||||
if [ -x "/etc/init.d/ngcp-mediaproxy-ng-daemon" ]; then
|
||||
if [ -x "$(which invoke-rc.d 2>/dev/null)" ]; then
|
||||
invoke-rc.d ngcp-mediaproxy-ng-daemon restart || exit $?
|
||||
else
|
||||
/etc/init.d/ngcp-mediaproxy-ng-daemon restart || exit $?
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
initscript_handler() {
|
||||
if [ -x "/etc/init.d/ngcp-mediaproxy-ng-daemon" ]; then
|
||||
update-rc.d ngcp-mediaproxy-ng-daemon defaults >/dev/null
|
||||
invoke-rc.d ngcp-mediaproxy-ng-daemon start || exit $?
|
||||
fi
|
||||
}
|
||||
|
||||
init_handler() {
|
||||
# just invoke init script wrappers on ce systems since
|
||||
# they do not provide ngcp-check_active and we don't
|
||||
# have to handle inactive nodes
|
||||
if ! [ -x "$(which ngcp-check_active 2>/dev/null)" ]; then
|
||||
restart_handler
|
||||
initscript_handler
|
||||
else # do not restart daemon on inactive node in pro systems
|
||||
if ngcp-check_active ; then
|
||||
echo "Active node detected, restarting ngcp-mediaproxy-ng-daemon"
|
||||
restart_handler
|
||||
else
|
||||
echo "Inactive node detected, ignoring request to restart ngcp-mediaproxy-ng-daemon"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
init_handler
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# postrm script for ngcp-mediaproxy-ng-daemon
|
||||
|
||||
set -e
|
||||
|
||||
removal_wrapper() {
|
||||
# remove the init script only on ce systems, as the
|
||||
# the pro system handle it inside the monitoring/HA setup
|
||||
if ! [ -x "$(which ngcp-check_active 2>/dev/null)" ]; then
|
||||
update-rc.d ngcp-mediaproxy-ng-daemon remove >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$1" = "purge" ] ; then
|
||||
removal_wrapper
|
||||
fi
|
||||
|
||||
exit 0
|
@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# prerm script for ngcp-mediaproxy-ng-daemon
|
||||
|
||||
set -e
|
||||
|
||||
stop_handler() {
|
||||
if [ -x "/etc/init.d/ngcp-mediaproxy-ng-daemon" ]; then
|
||||
invoke-rc.d ngcp-mediaproxy-ng-daemon stop || exit $?
|
||||
fi
|
||||
}
|
||||
|
||||
stop_daemon() {
|
||||
# just stop the service on ce systems because
|
||||
# the pro system handle it as part of their monitoring/HA setup
|
||||
if ! [ -x "$(which ngcp-check_active 2>/dev/null)" ]; then
|
||||
stop_handler
|
||||
else
|
||||
case "$2" in
|
||||
upgrade)
|
||||
echo "Detected upgrade procedure, not stopping ngcp-mediaproxy-ng-daemon service."
|
||||
;;
|
||||
remove|purge)
|
||||
stop_handler
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$1" = "prerm" ] ; then
|
||||
stop_daemon
|
||||
fi
|
||||
|
||||
exit 0
|
@ -0,0 +1 @@
|
||||
daemon/*.h /usr/include/mediaproxy-ng/
|
@ -0,0 +1 @@
|
||||
iptables-extension/libxt_MEDIAPROXY.so /lib/xtables/
|
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
package=ngcp-mediaproxy-ng-kernel-dkms
|
||||
name=ngcp-mediaproxy-ng
|
||||
|
||||
version=`dpkg-query -W -f='${Version}' "$package" \
|
||||
|rev|cut -d- -f2-|rev|cut -d':' -f2|tr -d "\n"`
|
||||
|
||||
isadded=`dkms status -m "$name" -v "$version"`
|
||||
|
||||
if [ "x${isadded}" = "x" ] ; then
|
||||
dkms add -m "$name" -v "$version"
|
||||
fi
|
||||
|
||||
if [ "$1" = 'configure' ] ; then
|
||||
dkms build -m "$name" -v "$version" && dkms install -m "$name" -v "$version" || true
|
||||
|
||||
# try to start the daemon
|
||||
if [ -x /etc/init.d/ngcp-mediaproxy-ng-daemon ] ; then
|
||||
invoke-rc.d ngcp-mediaproxy-ng-daemon start || true
|
||||
fi
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
|
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
package=ngcp-mediaproxy-ng-kernel-dkms
|
||||
name=ngcp-mediaproxy-ng
|
||||
|
||||
version=`dpkg-query -W -f='${Version}' "$package" \
|
||||
|rev|cut -d- -f2-|rev|cut -d':' -f2|tr -d "\n"`
|
||||
|
||||
# make sure it's not running
|
||||
if [ -x /etc/init.d/ngcp-mediaproxy-ng-daemon ] ; then
|
||||
invoke-rc.d ngcp-mediaproxy-ng-daemon stop || true
|
||||
fi
|
||||
|
||||
dkms remove -m "$name" -v "$version" --all || true
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
@ -0,0 +1 @@
|
||||
/usr/share/modass/packages/default.sh /usr/share/modass/overrides/ngcp-mediaproxy-ng-kernel-source
|
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# This file was originally written by Joey Hess and Craig Small.
|
||||
# As a special exception, when this file is copied by dh-make into a
|
||||
# dh-make output file, you may use that output file without restriction.
|
||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
# export DH_VERBOSE=1
|
||||
|
||||
b=$(CURDIR)/debian
|
||||
|
||||
## kernel package specific stuff
|
||||
# Name of the source package
|
||||
psource:=ngcp-mediaproxy-ng-kernel-source
|
||||
# Name of the dkms package
|
||||
pdkms:=ngcp-mediaproxy-ng-kernel-dkms
|
||||
# short upstream name, used for module source directory
|
||||
sname:=ngcp-mediaproxy-ng
|
||||
# Source version
|
||||
sversion:=$(shell dpkg-parsechangelog|grep "^Version:"|cut -d" " -f2|rev|cut -d- -f2-|rev|cut -d':' -f2)
|
||||
|
||||
PACKAGE=ngcp-mediaproxy-ng-kernel
|
||||
MA_DIR ?= /usr/share/modass
|
||||
-include $(MA_DIR)/include/generic.make
|
||||
-include $(MA_DIR)/include/common-rules.make
|
||||
|
||||
kdist_configure: prep-deb-files
|
||||
|
||||
kdist_clean: clean
|
||||
$(MAKE) $(MFLAGS) -f debian/rules clean
|
||||
## end of kernel package specific stuff
|
||||
|
||||
build: build-stamp
|
||||
|
||||
build-stamp:
|
||||
dh_testdir
|
||||
make -C iptables-extension
|
||||
make -C daemon
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
cd daemon && $(MAKE) clean && cd ..
|
||||
rm -f build-stamp
|
||||
rm -f iptables-extension/libxt_MEDIAPROXY.so
|
||||
rm -f daemon/mediaproxy-ng daemon/build_time.h daemon/.depend kernel-module/.xt_MEDIAPROXY.o.d
|
||||
rm -rf kernel-module/.tmp_versions
|
||||
dh_clean
|
||||
-rm -rf debian/build
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
|
||||
# Create the directories to install the source into
|
||||
dh_installdirs -p$(psource) usr/src/modules/$(sname)/debian
|
||||
dh_installdirs -p$(pdkms) usr/src/$(sname)-$(sversion)
|
||||
|
||||
# Copy only the driver source to the proper locations
|
||||
cd kernel-module && cp Makefile *.c *.h ../debian/$(psource)/usr/src/modules/$(sname)
|
||||
cd kernel-module && cp Makefile *.c *.h ../debian/$(pdkms)/usr/src/$(sname)-$(sversion)
|
||||
|
||||
# Copy the needed debian/ pieces to the proper location
|
||||
cp debian/*.modules.in* debian/$(psource)/usr/src/modules/$(sname)/debian
|
||||
cp debian/control debian/changelog debian/copyright debian/README.Debian \
|
||||
debian/compat debian/$(psource)/usr/src/modules/$(sname)/debian/
|
||||
install -m 0755 debian/rules.modules debian/$(psource)/usr/src/modules/$(sname)/debian/rules
|
||||
cd debian/$(psource)/usr/src && tar c modules | bzip2 -9 > $(sname).tar.bz2 && rm -rf modules
|
||||
|
||||
# Prepare dkms.conf from the dkms.conf.in template
|
||||
sed "s/__VERSION__/$(sversion)/g" debian/dkms.conf.in > debian/$(pdkms)/usr/src/$(sname)-$(sversion)/dkms.conf
|
||||
|
||||
dh_install
|
||||
|
||||
%:
|
||||
@echo "--- Building: $@"
|
||||
dh_installdirs -p$@ -P$(b)/$@
|
||||
dh_link -p$@ -P$(b)/$@
|
||||
dh_installdocs -p$@ -P$(b)/$@
|
||||
dh_installchangelogs -p$@ -P$(b)/$@
|
||||
dh_installinit -p$@ -P$(b)/$@
|
||||
dh_install -p$@ -P$(b)/$@
|
||||
dh_strip -p$@ -P$(b)/$@ --keep-debug
|
||||
dh_compress -p$@ -P$(b)/$@
|
||||
dh_fixperms -p$@ -P$(b)/$@
|
||||
dh_makeshlibs -p$@ -P$(b)/$@ -V
|
||||
dh_installdeb -p$@ -P$(b)/$@
|
||||
dh_shlibdeps -p$@ -P$(b)/$@
|
||||
dh_installdebconf -p$@ -P$(b)/$@
|
||||
dh_gencontrol -p$@ -P$(b)/$@
|
||||
dh_md5sums -p$@ -P$(b)/$@
|
||||
dh_builddeb -p$@ -P$(b)/$@
|
||||
|
||||
# Build architecture dependant packages
|
||||
binary-arch: install \
|
||||
ngcp-mediaproxy-ng-iptables ngcp-mediaproxy-ng
|
||||
|
||||
# Build architecture independant packages
|
||||
binary-indep: build install \
|
||||
ngcp-mediaproxy-ng-daemon ngcp-mediaproxy-ng-kernel-dkms ngcp-mediaproxy-ng-kernel-source ngcp-mediaproxy-ng-dev
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install kdist kdist_configure kdist_image kdist_clean
|
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
CFLAGS ?= -Wall -g
|
||||
|
||||
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
# Name of the source package
|
||||
psource:=ngcp-mediaproxy-ng-kernel-source
|
||||
|
||||
# The short upstream name, used for the module source directory
|
||||
sname:=ngcp-mediaproxy-ng
|
||||
|
||||
### KERNEL SETUP
|
||||
### Setup the stuff needed for making kernel module packages
|
||||
### taken from /usr/share/kernel-package/sample.module.rules
|
||||
|
||||
# prefix of the target package name
|
||||
PACKAGE=ngcp-mediaproxy-ng-kernel-modules
|
||||
# modifieable for experiments or debugging m-a
|
||||
MA_DIR ?= /usr/share/modass
|
||||
# load generic variable handling
|
||||
-include $(MA_DIR)/include/generic.make
|
||||
# load default rules, including kdist, kdist_image, ...
|
||||
-include $(MA_DIR)/include/common-rules.make
|
||||
|
||||
# module assistant calculates all needed things for us and sets
|
||||
# following variables:
|
||||
# KSRC (kernel source directory), KVERS (kernel version string), KDREV
|
||||
# (revision of the Debian kernel-image package), CC (the correct
|
||||
# compiler), VERSION (the final package version string), PKGNAME (full
|
||||
# package name with KVERS included), DEB_DESTDIR (path to store DEBs)
|
||||
|
||||
# The kdist_configure target is called by make-kpkg modules_config and
|
||||
# by kdist* rules by dependency. It should configure the module so it is
|
||||
# ready for compilation (mostly useful for calling configure).
|
||||
# prep-deb-files from module-assistant creates the neccessary debian/ files
|
||||
kdist_configure: prep-deb-files
|
||||
|
||||
# the kdist_clean target is called by make-kpkg modules_clean and from
|
||||
# kdist* rules. It is responsible for cleaning up any changes that have
|
||||
# been made by the other kdist_commands (except for the .deb files created)
|
||||
kdist_clean: clean
|
||||
$(MAKE) $(MFLAGS) -f debian/rules clean
|
||||
#
|
||||
### end KERNEL SETUP
|
||||
|
||||
# the binary-modules rule is invoked by module-assistant while processing the
|
||||
# kdist* targets. It is called by module-assistant or make-kpkg and *not*
|
||||
# during a normal build
|
||||
binary-modules: prep-deb-files
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
|
||||
# Build the module
|
||||
$(MAKE) modules KSRC=$(KSRC) KVER=$(KVERS)
|
||||
|
||||
# Install the module
|
||||
install -D -m 0644 xt_MEDIAPROXY.ko debian/$(PACKAGE)-$(KVERS)/lib/modules/$(KVERS)/extra/xt_MEDIAPROXY.ko
|
||||
|
||||
dh_installdocs
|
||||
dh_installchangelogs
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
dh_installmodules
|
||||
dh_installdeb
|
||||
dh_gencontrol -- -v$(VERSION)
|
||||
dh_md5sums
|
||||
dh_builddeb --destdir=$(DEB_DESTDIR)
|
||||
dh_clean -k
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
$(MAKE) clean
|
||||
dh_clean
|
||||
|
||||
.PHONY: clean binary-modules kdist kdist_configure kdist_image kdist_clean
|
@ -0,0 +1,37 @@
|
||||
CFLAGS = -O2 -Wall -shared -fPIC
|
||||
CFLAGS += -DMEDIAPROXY_VERSION="\"$(shell dpkg-parsechangelog -l../debian/changelog | awk '/^Version: / {print $$2}')\""
|
||||
|
||||
XTABLES = $(shell test -e /usr/include/xtables.h && echo 1)
|
||||
IPTABLES = $(shell test -e /usr/include/iptables.h && echo 1)
|
||||
IP6TABLES = $(shell test -e /usr/include/ip6tables.h && echo 1)
|
||||
|
||||
.PHONY: all module clean
|
||||
|
||||
all: module
|
||||
|
||||
ifeq ($(XTABLES),1)
|
||||
|
||||
module: libxt_MEDIAPROXY.so
|
||||
|
||||
libxt_MEDIAPROXY.so: libxt_MEDIAPROXY.c
|
||||
gcc $(CFLAGS) -o libxt_MEDIAPROXY.so libxt_MEDIAPROXY.c
|
||||
|
||||
else
|
||||
|
||||
ifeq ($(IPTABLES),1)
|
||||
module: libipt_MEDIAPROXY.so
|
||||
endif
|
||||
ifeq ($(IP6TABLES),1)
|
||||
module: libip6t_MEDIAPROXY.so
|
||||
endif
|
||||
|
||||
libipt_MEDIAPROXY.so: libxt_MEDIAPROXY.c
|
||||
gcc $(CFLAGS) -D__ipt -o libipt_MEDIAPROXY.so libxt_MEDIAPROXY.c
|
||||
|
||||
libip6t_MEDIAPROXY.so: libxt_MEDIAPROXY.c
|
||||
gcc $(CFLAGS) -D__ip6t -o libip6t_MEDIAPROXY.so libxt_MEDIAPROXY.c
|
||||
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f libxt_MEDIAPROXY.so libipt_MEDIAPROXY.so libip6t_MEDIAPROXY.so
|
@ -0,0 +1,179 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#if defined(__ipt)
|
||||
#include <iptables.h>
|
||||
#elif defined(__ip6t)
|
||||
#include <ip6tables.h>
|
||||
#else
|
||||
#include <xtables.h>
|
||||
#endif
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
|
||||
#if defined(__ipt)
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#elif defined(__ip6t)
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
#else
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#endif
|
||||
|
||||
#include "../kernel-module/xt_MEDIAPROXY.h"
|
||||
|
||||
|
||||
static void help(void) {
|
||||
printf(
|
||||
"MEDIAPROXY (version %s) target options:\n"
|
||||
" --id <id>\n"
|
||||
" Unique ID for this instance\n",
|
||||
MEDIAPROXY_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
#if defined(__ipt)
|
||||
static int parse(int c,
|
||||
char **argv,
|
||||
int invert,
|
||||
unsigned int *flags,
|
||||
const struct ipt_entry *entry,
|
||||
struct ipt_entry_target **target) {
|
||||
#elif defined(__ip6t)
|
||||
static int parse(int c,
|
||||
char **argv,
|
||||
int invert,
|
||||
unsigned int *flags,
|
||||
const struct ip6t_entry *entry,
|
||||
struct ip6t_entry_target **target) {
|
||||
#else
|
||||
static int parse(int c,
|
||||
char **argv,
|
||||
int invert,
|
||||
unsigned int *flags,
|
||||
const void *entry,
|
||||
struct xt_entry_target **target) {
|
||||
#endif
|
||||
|
||||
struct xt_mediaproxy_info *info = (void *) (*target)->data;
|
||||
|
||||
if (c == '1') {
|
||||
info->id = atoi(optarg);
|
||||
if (flags)
|
||||
*flags = 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void final_check(unsigned int flags) {
|
||||
#if defined(__ipt) || defined(__ip6t)
|
||||
if (!flags)
|
||||
exit_error(PARAMETER_PROBLEM, "You must specify --id");
|
||||
#else
|
||||
if (!flags)
|
||||
xtables_error(PARAMETER_PROBLEM, "You must specify --id");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__ipt)
|
||||
static void print(const struct ipt_ip *ip, const struct xt_entry_target *target, int numeric) {
|
||||
#elif defined(__ip6t)
|
||||
static void print(const struct ip6t_ip6 *ip, const struct xt_entry_target *target, int numeric) {
|
||||
#else
|
||||
static void print(const void *ip, const struct xt_entry_target *target, int numeric) {
|
||||
#endif
|
||||
struct xt_mediaproxy_info *info = (void *) target->data;
|
||||
|
||||
printf("id %u", info->id);
|
||||
}
|
||||
|
||||
#if defined(__ipt)
|
||||
static void save(const struct ipt_ip *ip, const struct xt_entry_target *target) {
|
||||
#elif defined(__ip6t)
|
||||
static void save(const struct ip6t_ip6 *ip, const struct xt_entry_target *target) {
|
||||
#else
|
||||
static void save(const void *ip, const struct xt_entry_target *target) {
|
||||
#endif
|
||||
struct xt_mediaproxy_info *info = (void *) target->data;
|
||||
|
||||
printf("--id %u", info->id);
|
||||
}
|
||||
|
||||
static struct option opts[] = {
|
||||
{ "id", 1, NULL, '1' },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
||||
#if defined(__ipt)
|
||||
static struct iptables_target mediaproxy4 = {
|
||||
.name = "MEDIAPROXY",
|
||||
.version = "1.3.6",
|
||||
.size = IPT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.userspacesize = IPT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
#elif defined(__ip6t)
|
||||
static struct ip6tables_target mediaproxy6 = {
|
||||
.name = "MEDIAPROXY",
|
||||
.version = "1.3.6",
|
||||
.size = IP6T_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.userspacesize = IP6T_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
#else
|
||||
static struct xtables_target mediaproxy4 = {
|
||||
.name = "MEDIAPROXY",
|
||||
.family = NFPROTO_IPV4,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
|
||||
static struct xtables_target mediaproxy6 = {
|
||||
.name = "MEDIAPROXY",
|
||||
.family = NFPROTO_IPV6,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct xt_mediaproxy_info)),
|
||||
.help = help,
|
||||
.parse = parse,
|
||||
.final_check = final_check,
|
||||
.print = print,
|
||||
.save = save,
|
||||
.extra_opts = opts,
|
||||
};
|
||||
#endif
|
||||
|
||||
void _init(void) {
|
||||
#if defined(__ipt)
|
||||
register_target(&mediaproxy4);
|
||||
#elif defined(__ip6t)
|
||||
register_target6(&mediaproxy6);
|
||||
#else
|
||||
xtables_register_target(&mediaproxy4);
|
||||
xtables_register_target(&mediaproxy6);
|
||||
#endif
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
PWD := $(shell pwd)
|
||||
KSRC ?= /lib/modules/$(shell uname -r)/build
|
||||
KBUILD := $(KSRC)
|
||||
ifeq ($(origin MEDIAPROXY_VERSION), undefined)
|
||||
MEDIAPROXY_VERSION := $(shell dpkg-parsechangelog -l../debian/changelog | awk '/^Version: / {print $$2}')
|
||||
export MEDIAPROXY_VERSION
|
||||
endif
|
||||
EXTRA_CFLAGS += -DMEDIAPROXY_VERSION="\"$(MEDIAPROXY_VERSION)\"" -D__MP_EXTERNAL
|
||||
|
||||
obj-m += xt_MEDIAPROXY.o
|
||||
|
||||
.PHONY: modules clean patch
|
||||
|
||||
modules:
|
||||
make -C $(KBUILD) M=$(PWD) O=$(KBUILD) modules
|
||||
|
||||
clean:
|
||||
make -C $(KBUILD) M=$(PWD) clean
|
||||
|
||||
patch:
|
||||
../utils/patch-kernel magic "$(PWD)" "$(KERNEL)" "$(MEDIAPROXY_VERSION)"
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,85 @@
|
||||
#ifndef IPT_RTPPROXY_H
|
||||
#define IPT_RTPPROXY_H
|
||||
|
||||
struct xt_mediaproxy_info {
|
||||
u_int32_t id;
|
||||
};
|
||||
|
||||
struct mediaproxy_stats {
|
||||
u_int64_t packets;
|
||||
u_int64_t bytes;
|
||||
u_int64_t errors;
|
||||
};
|
||||
|
||||
struct mp_address {
|
||||
int family;
|
||||
union {
|
||||
unsigned char ipv6[16];
|
||||
u_int32_t ipv4;
|
||||
|
||||
unsigned char u8[16];
|
||||
u_int16_t u16[8];
|
||||
u_int32_t u32[4];
|
||||
};
|
||||
u_int16_t port;
|
||||
};
|
||||
|
||||
struct mediaproxy_target_info {
|
||||
u_int16_t target_port;
|
||||
|
||||
struct mp_address src_addr;
|
||||
struct mp_address dst_addr;
|
||||
|
||||
struct mp_address mirror_addr;
|
||||
|
||||
unsigned char tos;
|
||||
};
|
||||
|
||||
struct mediaproxy_message {
|
||||
enum {
|
||||
MMG_NOOP = 1,
|
||||
MMG_ADD,
|
||||
MMG_DEL,
|
||||
MMG_UPDATE,
|
||||
} cmd;
|
||||
|
||||
struct mediaproxy_target_info target;
|
||||
};
|
||||
|
||||
struct mediaproxy_list_entry {
|
||||
struct mediaproxy_target_info target;
|
||||
struct mediaproxy_stats stats;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct mediaproxy_target {
|
||||
atomic_t refcnt;
|
||||
u_int32_t table;
|
||||
struct mediaproxy_target_info target;
|
||||
|
||||
spinlock_t lock;
|
||||
struct mediaproxy_stats stats;
|
||||
};
|
||||
|
||||
struct mediaproxy_table {
|
||||
atomic_t refcnt;
|
||||
rwlock_t target_lock;
|
||||
pid_t pid;
|
||||
|
||||
u_int32_t id;
|
||||
struct proc_dir_entry *proc;
|
||||
struct proc_dir_entry *status;
|
||||
struct proc_dir_entry *control;
|
||||
struct proc_dir_entry *list;
|
||||
struct proc_dir_entry *blist;
|
||||
|
||||
struct mediaproxy_target **target[256];
|
||||
|
||||
unsigned int buckets;
|
||||
unsigned int targets;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,246 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Socket;
|
||||
|
||||
$| = 1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
my $local_ip = '192.168.1.90';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sub brk {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
sub mp_msg {
|
||||
my ($cmd) = @_;
|
||||
|
||||
my $fd;
|
||||
socket($fd, AF_INET, SOCK_STREAM, 0) or die;
|
||||
connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die;
|
||||
my $old = select($fd);
|
||||
$| = 1;
|
||||
print("$cmd\n");
|
||||
my $ret = <$fd>;
|
||||
select($old);
|
||||
close($fd);
|
||||
chomp($ret);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub udp_sock {
|
||||
while (1) {
|
||||
my $fd;
|
||||
socket($fd, AF_INET, SOCK_DGRAM, 0) or die;
|
||||
bind($fd, sockaddr_in(0, inet_aton('0.0.0.0'))) or die;
|
||||
my $sa = getsockname($fd);
|
||||
my ($port, $addr) = sockaddr_in($sa);
|
||||
(($port % 2) == 0) or next;
|
||||
return ($fd, $port);
|
||||
}
|
||||
}
|
||||
|
||||
sub send_rcv {
|
||||
my ($sendfd, $sendtoip, $sendtoport, $recvfd) = @_;
|
||||
print("sending to $sendtoip:$sendtoport... ");
|
||||
my $pkt = join('',map(rand,1..10));
|
||||
send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die;
|
||||
my $inc;
|
||||
{
|
||||
local $SIG{ALRM} = sub {
|
||||
print("timeout!\n");
|
||||
};
|
||||
alarm(1);
|
||||
recv($recvfd, $inc, length($pkt), 0);
|
||||
alarm(0);
|
||||
}
|
||||
$inc eq $pkt or print("NOT received packed ok\n"), return;
|
||||
print("received packet ok\n");
|
||||
}
|
||||
|
||||
sub send_rcv4 {
|
||||
for (1 .. 4) {
|
||||
send_rcv(@_[0,1,2,3]);
|
||||
sleep(1);
|
||||
send_rcv(@_[3,4,5,0]);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
sub sim_req_lk {
|
||||
my ($method, $callid, $ip, $port, $fromtag, $totag) = @_;
|
||||
|
||||
$totag or $totag = '';
|
||||
|
||||
my $ret = mp_msg("$method $callid $ip:$port:audio 192.168.101.11 80.110.1.48 remote 212.41.253.181 remote CS2000_NGSS/9.0 info=domain:voip.sipwise.local,from:431960681661\@80.110.1.48:5060,totag:$totag,to:43720890289\@77.244.249.84:5060,fromtag:$fromtag");
|
||||
|
||||
return split(/ /, $ret);
|
||||
}
|
||||
sub sim_rq {
|
||||
return sim_req_lk("request", @_);
|
||||
}
|
||||
sub sim_lk {
|
||||
return sim_req_lk("lookup", @_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
my $callid1 = join('',map(rand,1..2));
|
||||
my $fromtag1 = join('',map(rand,1..4));
|
||||
my $totag1 = join('',map(rand,1..4));
|
||||
my $callid2 = join('',map(rand,1..2));
|
||||
my $fromtag2 = join('',map(rand,1..4));
|
||||
my $totag2 = join('',map(rand,1..4));
|
||||
|
||||
my ($client1, $lp1) = udp_sock();
|
||||
my ($client2, $lp2) = udp_sock();
|
||||
my ($client3, $lp3) = udp_sock();
|
||||
my ($client4, $lp4) = udp_sock();
|
||||
|
||||
print("call-ids: $callid1 & $callid2\n");
|
||||
print("opened ports $lp1 [A->B], $lp2 [B->A], $lp3 [B->C], $lp4 [C->B] as RTP clients\n");
|
||||
brk();
|
||||
|
||||
print("CALL 1: A calling B\n");
|
||||
print("A tells B: send RTP to $lp1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip1, $mpport1) = sim_rq($callid1, $local_ip, $lp1, $fromtag1);
|
||||
print("mediaproxy: tell B to send to $mpport1 instead of $lp1\n");
|
||||
brk();
|
||||
|
||||
print("B tells A: send RTP to $lp2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip2, $mpport2) = sim_lk($callid1, $local_ip, $lp2, $fromtag1, $totag1);
|
||||
print("mediaproxy: tell A to send to $mpport2 instead of $lp2\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($client2, $mpip1, $mpport1, $client1, $mpip2, $mpport2);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print("B puts A on hold\n");
|
||||
print("B tells A: send RTP to 0.0.0.0\n");
|
||||
|
||||
brk();
|
||||
|
||||
my ($mpip3, $mpport3) = sim_rq($callid1, '0.0.0.0', $lp2, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell A to send to $mpip3:$mpport3 instead of 0.0.0.0:$lp2\n");
|
||||
brk();
|
||||
|
||||
print("A tells B: send RTP to 0.0.0.0\n");
|
||||
brk();
|
||||
|
||||
my ($mpip4, $mpport4) = sim_lk($callid1, '0.0.0.0', $lp1, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell B to send to $mpip4:$mpport4 instead of 0.0.0.0:$lp1\n");
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print("CALL 2: B calling C\n");
|
||||
print("B tells C: send RTP to $lp3\n");
|
||||
|
||||
brk();
|
||||
|
||||
my ($mpip5, $mpport5) = sim_rq($callid2, $local_ip, $lp3, $fromtag2);
|
||||
print("mediaproxy: tell C to send to $mpport5 instead of $lp3\n");
|
||||
brk();
|
||||
|
||||
print("C tells B: send RTP to $lp4\n");
|
||||
brk();
|
||||
|
||||
my ($mpip6, $mpport6) = sim_lk($callid2, $local_ip, $lp4, $fromtag2, $totag2);
|
||||
print("mediaproxy: tell B to send to $mpport6 instead of $lp4\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($client4, $mpip5, $mpport5, $client3, $mpip6, $mpport6);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print("B un-holds A\n");
|
||||
print("B tells A: send RTP to $lp2\n");
|
||||
|
||||
brk();
|
||||
|
||||
my ($mpip7, $mpport7) = sim_rq($callid1, $local_ip, $lp2, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell A to send to $mpport7 instead of $lp2\n");
|
||||
brk();
|
||||
|
||||
print("A tells B: send RTP to $lp1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip8, $mpport8) = sim_lk($callid1, $local_ip, $lp1, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell B to send to $mpport8 instead of $lp1\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($client2, $mpip1, $mpport1, $client1, $mpip2, $mpport2);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print("CONNECT: B connects A to C\n");
|
||||
print("B tells C [call 2]: send RTP to $mpip8:$mpport8\n");
|
||||
|
||||
brk();
|
||||
|
||||
my ($mpip9, $mpport9) = sim_rq($callid2, $mpip8, $mpport8, $fromtag2, $totag2);
|
||||
print("mediaproxy: tell C to send to $mpport9 instead of $mpip8:$mpport8\n");
|
||||
brk();
|
||||
|
||||
print("C tells B: send RTP to $lp4\n");
|
||||
my ($mpip10, $mpport10) = sim_lk($callid2, $local_ip, $lp4, $fromtag2, $totag2);
|
||||
print("mediaproxy: tell B to send to $mpport10 instead of $lp4\n");
|
||||
brk();
|
||||
|
||||
|
||||
print("B tells A [call 1]: send RTP to $mpip10:$mpport10\n");
|
||||
|
||||
my ($mpip11, $mpport11) = sim_rq($callid1, $mpip10, $mpport10, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell A to send to $mpport11 instead of $mpip10:$mpport10\n");
|
||||
brk();
|
||||
|
||||
if (1) {
|
||||
###### error trigger
|
||||
send_rcv($client1, $mpip11, $mpport11, $client4);
|
||||
}
|
||||
|
||||
print("A tells B: send RTP to $lp1\n");
|
||||
my ($mpip12, $mpport12) = sim_lk($callid1, $local_ip, $lp1, $totag1, $fromtag1);
|
||||
print("mediaproxy: tell B to send to $mpport12 instead of $lp1\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($client4, $mpip9, $mpport9, $client1, $mpip11, $mpport11);
|
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
use Socket6;
|
||||
|
||||
my $t = $ARGV[0] || "0";
|
||||
|
||||
my $format = 'SS ia16SS ia16SS ia16SS CCCC LLLLLL';
|
||||
my $len = length(pack($format, (0) x 100));
|
||||
|
||||
open(X, "<", "/proc/mediaproxy/$t/blist") or die;
|
||||
my $buf;
|
||||
while (sysread(X, $buf, $len)) {
|
||||
my @b = unpack($format, $buf);
|
||||
for (2,6,10) {
|
||||
if ($b[$_] == AF_INET) {
|
||||
$b[$_ + 1] = inet_ntoa($b[$_ + 1]);
|
||||
}
|
||||
elsif ($b[$_] == AF_INET6) {
|
||||
$b[$_ + 1] = inet_ntop(AF_INET6, $b[$_ + 1]);
|
||||
}
|
||||
elsif ($b[$_] == 0) {
|
||||
$b[$_ + 1] = '---';
|
||||
}
|
||||
}
|
||||
for (18, 20, 22) {
|
||||
$b[$_] += $b[$_ + 1] * 2**32;
|
||||
}
|
||||
printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%u] [%llu %llu %llu]\n", @b[0,3,4,7,8,11,12,14,18,20,22]);
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
use Socket6;
|
||||
|
||||
my %cmds = (noop => 1, add => 2, delete => 3, update => 4);
|
||||
$| = 1;
|
||||
|
||||
open(F, "> /proc/mediaproxy/0/control") or die;
|
||||
{
|
||||
my $x = select(F);
|
||||
$| = 1;
|
||||
select($x);
|
||||
}
|
||||
|
||||
sub mp_address {
|
||||
my ($fam, $addr, $port) = @_;
|
||||
|
||||
if ($fam eq 'inet') {
|
||||
return pack('i a4 a12 S S', 2, inet_aton($addr), '', $port, 0);
|
||||
}
|
||||
if ($fam eq 'inet6') {
|
||||
return pack('i a16 S S', 10, inet_pton(AF_INET6, $addr), $port, 0);
|
||||
}
|
||||
if ($fam eq '') {
|
||||
return pack('i a16 S S', 0, '', 0, 0);
|
||||
}
|
||||
|
||||
die;
|
||||
}
|
||||
sub mediaproxy_message {
|
||||
my ($cmd, $target_port,
|
||||
$src_addr_family, $src_addr_addr, $src_addr_port,
|
||||
$dst_addr_family, $dst_addr_addr, $dst_addr_port,
|
||||
$mirror_addr_family, $mirror_addr_addr, $mirror_addr_port,
|
||||
$tos) = @_;
|
||||
|
||||
my $ret = '';
|
||||
|
||||
$ret .= pack('I SS', $cmds{$cmd}, $target_port, 0);
|
||||
$ret .= mp_address($src_addr_family, $src_addr_addr, $src_addr_port);
|
||||
$ret .= mp_address($dst_addr_family, $dst_addr_addr, $dst_addr_port);
|
||||
$ret .= mp_address($mirror_addr_family, $mirror_addr_addr, $mirror_addr_port);
|
||||
$ret .= pack('C CS', $tos, 0, 0);
|
||||
}
|
||||
|
||||
my $sleep = 5;
|
||||
#my @src = qw(inet 10.15.20.61);
|
||||
#my @dst = qw(inet 10.15.20.58);
|
||||
my @src = qw(inet6 2a00:4600:1:0:a00:27ff:feb0:f7fe);
|
||||
my @dst = qw(inet6 2a00:4600:1:0:6884:adff:fe98:6ac5);
|
||||
my @nul = ('', '', '');
|
||||
|
||||
print("add 9876 -> 1234/6543\n");
|
||||
syswrite(F, mediaproxy_message('add', 9876, @src, 1234, @dst, 6543, @nul, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("add fail\n");
|
||||
syswrite(F, mediaproxy_message('add', 9876, @src, 1234, @dst, 6543, @dst, 6789, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("update 9876 -> 1234/6543 & 6789\n");
|
||||
syswrite(F, mediaproxy_message('update', 9876, @src, 1234, @dst, 6543, @dst, 6789, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("update 9876 -> 2345/7890 & 4321\n");
|
||||
syswrite(F, mediaproxy_message('update', 9876, @src, 2345, @dst, 7890, @dst, 4321, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("add fail\n");
|
||||
syswrite(F, mediaproxy_message('add', 9876, @src, 1234, @dst, 6543, @dst, 6789, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("update 9876 -> 1234/6543\n");
|
||||
syswrite(F, mediaproxy_message('update', 9876, @src, 1234, @dst, 6543, @nul, 184));
|
||||
sleep($sleep);
|
||||
|
||||
print("delete\n");
|
||||
syswrite(F, mediaproxy_message('delete', 9876, @nul, @nul, @nul, 0));
|
||||
sleep($sleep);
|
||||
|
||||
print("delete fail\n");
|
||||
syswrite(F, mediaproxy_message('delete', 9876, @nul, @nul, @nul, 0));
|
||||
sleep($sleep);
|
||||
|
||||
print("update fail\n");
|
||||
syswrite(F, mediaproxy_message('update', 9876, @src, 1234, @dst, 6543, @nul, 184));
|
||||
sleep($sleep);
|
||||
|
||||
close(F);
|
@ -0,0 +1,370 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Socket;
|
||||
|
||||
$| = 1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
my $local_ip = '192.168.1.90';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sub brk {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
sub mp_msg {
|
||||
my ($cmd) = @_;
|
||||
|
||||
my $fd;
|
||||
socket($fd, AF_INET, SOCK_STREAM, 0) or die;
|
||||
connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die;
|
||||
my $old = select($fd);
|
||||
$| = 1;
|
||||
print("$cmd\n");
|
||||
my $ret = <$fd>;
|
||||
select($old);
|
||||
close($fd);
|
||||
chomp($ret);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub udp_sock {
|
||||
while (1) {
|
||||
my $fd;
|
||||
socket($fd, AF_INET, SOCK_DGRAM, 0) or die;
|
||||
bind($fd, sockaddr_in(0, inet_aton('0.0.0.0'))) or die;
|
||||
my $sa = getsockname($fd);
|
||||
my ($port, $addr) = sockaddr_in($sa);
|
||||
(($port % 2) == 0) or next;
|
||||
return ($fd, $port);
|
||||
}
|
||||
}
|
||||
|
||||
sub send_rcv {
|
||||
my ($sendfd, $sendtoip, $sendtoport, $recvfd) = @_;
|
||||
my $laddr = getsockname($sendfd);
|
||||
my ($lport, $lip) = sockaddr_in($laddr);
|
||||
print("local port $lport sending to $sendtoip:$sendtoport... ");
|
||||
my $pkt = join('',map(rand,1..10));
|
||||
send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die;
|
||||
my ($inc, $addr);
|
||||
{
|
||||
local $SIG{ALRM} = sub {
|
||||
print("timeout!\n");
|
||||
return;
|
||||
};
|
||||
alarm(1);
|
||||
$addr = recv($recvfd, $inc, length($pkt), 0);
|
||||
alarm(0);
|
||||
}
|
||||
$inc eq $pkt or print("did NOT receive packet\n"), return;
|
||||
my ($port, $ip) = sockaddr_in($addr);
|
||||
$laddr = getsockname($recvfd);
|
||||
($lport, $lip) = sockaddr_in($laddr);
|
||||
print("received packet ok on port $lport, from port $port\n");
|
||||
}
|
||||
|
||||
sub send_rcv_brk {
|
||||
send_rcv(@_);
|
||||
brk();
|
||||
}
|
||||
|
||||
sub send_rcv4 {
|
||||
for (1 .. 4) {
|
||||
send_rcv(@_[0,1,2,3]);
|
||||
sleep(1);
|
||||
send_rcv(@_[3,4,5,0]);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
sub sim_req_lk {
|
||||
my ($method, $callid, $ip, $port, $fromtag, $totag) = @_;
|
||||
|
||||
$totag or $totag = '';
|
||||
|
||||
my $ret = mp_msg("$method $callid $ip:$port:audio 192.168.101.11 80.110.1.48 remote 212.41.253.181 remote CS2000_NGSS/9.0 info=domain:voip.sipwise.local,from:431960681661\@80.110.1.48:5060,totag:$totag,to:43720890289\@77.244.249.84:5060,fromtag:$fromtag");
|
||||
|
||||
return split(/ /, $ret);
|
||||
}
|
||||
sub sim_rq {
|
||||
return sim_req_lk("request", @_);
|
||||
}
|
||||
sub sim_lk {
|
||||
return sim_req_lk("lookup", @_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
my $callid = join('',map(rand,1..2));
|
||||
my $fromtag = join('',map(rand,1..4));
|
||||
my $totag = join('',map(rand,1..4));
|
||||
|
||||
my ($client1, $lp1) = udp_sock();
|
||||
my ($client2, $lp2) = udp_sock();
|
||||
|
||||
print("call-id: $callid\n");
|
||||
print("opened ports $lp1 [A->B], $lp2 [B->A] as RTP clients\n");
|
||||
brk();
|
||||
|
||||
{
|
||||
print("A tells B: send RTP to $lp1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip1, $mpport1) = sim_rq($callid, $local_ip, $lp1, $fromtag);
|
||||
print("mediaproxy: tell B to send to $mpport1 instead of $lp1\n");
|
||||
brk();
|
||||
|
||||
print("B tells A: send RTP to $lp2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip2, $mpport2) = sim_lk($callid, $local_ip, $lp2, $fromtag, $totag);
|
||||
print("mediaproxy: tell A to send to $mpport2 instead of $lp2\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($client2, $mpip1, $mpport1, $client1, $mpip2, $mpport2);
|
||||
|
||||
brk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for my $case (0 .. 3) {
|
||||
|
||||
|
||||
|
||||
|
||||
my $sub;
|
||||
|
||||
if ($case == 0) {
|
||||
$sub = sub { ; };
|
||||
}
|
||||
elsif ($case == 1) {
|
||||
$sub = sub {
|
||||
print("\tchanging ports on client 1\n");
|
||||
($client1, $lp1) = udp_sock();
|
||||
};
|
||||
}
|
||||
elsif ($case == 2) {
|
||||
$sub = sub {
|
||||
print("\tchanging ports on client 2\n");
|
||||
($client2, $lp2) = udp_sock();
|
||||
};
|
||||
}
|
||||
elsif ($case == 3) {
|
||||
$sub = sub {
|
||||
print("\tchanging ports on client 1 and 2\n");
|
||||
($client1, $lp1) = udp_sock();
|
||||
($client2, $lp2) = udp_sock();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
my @forward = ('A', 'B', \$lp1, \$lp2, \$client1, \$client2, $fromtag, $totag);
|
||||
my @backward = ('B', 'A', \$lp2, \$lp1, \$client2, \$client1, $totag, $fromtag);
|
||||
my ($src, $dst, $p1, $p2, $c1, $c2, $ft, $tt);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for my $tuple (\@forward, \@backward) {
|
||||
($src, $dst, $p1, $p2, $c1, $c2, $ft, $tt) = @$tuple;
|
||||
|
||||
$sub->();
|
||||
print("\n\n\n");
|
||||
print("re-invite coming from $src with no intermediate traffic\n");
|
||||
|
||||
print("$src tells $dst: send RTP to $$p1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip1, $mpport1) = sim_rq($callid, $local_ip, $$p1, $ft);
|
||||
print("mediaproxy: tell $dst to send to $mpport1 instead of $$p1\n");
|
||||
brk();
|
||||
|
||||
print("$dst tells $src: send RTP to $$p2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip2, $mpport2) = sim_lk($callid, $local_ip, $$p2, $ft, $tt);
|
||||
print("mediaproxy: tell $src to send to $mpport2 instead of $$p2\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($$c2, $mpip1, $mpport1, $$c1, $mpip2, $mpport2);
|
||||
|
||||
brk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for my $tuple (\@forward, \@backward) {
|
||||
($src, $dst, $p1, $p2, $c1, $c2, $ft, $tt) = @$tuple;
|
||||
|
||||
$sub->();
|
||||
print("\n\n\n");
|
||||
print("re-invite coming from $src with intermediate traffic from $dst to the new port only\n");
|
||||
|
||||
print("$src tells $dst: send RTP to $$p1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip1, $mpport1) = sim_rq($callid, $local_ip, $$p1, $ft);
|
||||
print("mediaproxy: tell $dst to send to $mpport1 instead of $$p1\n");
|
||||
brk();
|
||||
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c2, $mpip1, $mpport1, $$c1);
|
||||
}
|
||||
|
||||
print("$dst tells $src: send RTP to $$p2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip2, $mpport2) = sim_lk($callid, $local_ip, $$p2, $ft, $tt);
|
||||
print("mediaproxy: tell $src to send to $mpport2 instead of $$p2\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($$c2, $mpip1, $mpport1, $$c1, $mpip2, $mpport2);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$sub->();
|
||||
print("\n\n\n");
|
||||
print("re-invite coming from $src with intermediate traffic from both sides to both old and new ports\n");
|
||||
|
||||
print("$src tells $dst: send RTP to $$p1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip3, $mpport3) = sim_rq($callid, $local_ip, $$p1, $ft);
|
||||
print("mediaproxy: tell $dst to send to $mpport3 instead of $$p1\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($$c2, $mpip1, $mpport1, $$c1, $mpip2, $mpport2);
|
||||
print("switching to new port...\n");
|
||||
send_rcv4($$c2, $mpip3, $mpport3, $$c1, $mpip2, $mpport2);
|
||||
|
||||
print("$dst tells $src: send RTP to $$p2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip4, $mpport4) = sim_lk($callid, $local_ip, $$p2, $ft, $tt);
|
||||
print("mediaproxy: tell $src to send to $mpport4 instead of $$p2\n");
|
||||
brk();
|
||||
|
||||
send_rcv4($$c2, $mpip3, $mpport3, $$c1, $mpip2, $mpport2);
|
||||
print("switching to new port...\n");
|
||||
send_rcv4($$c2, $mpip3, $mpport3, $$c1, $mpip4, $mpport4);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$sub->();
|
||||
print("\n\n\n");
|
||||
print("re-invite coming from $src with intermediate traffic from $dst only to both old and new ports\n");
|
||||
|
||||
print("$src tells $dst: send RTP to $$p1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip5, $mpport5) = sim_rq($callid, $local_ip, $$p1, $ft);
|
||||
print("mediaproxy: tell $dst to send to $mpport5 instead of $$p1\n");
|
||||
brk();
|
||||
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c2, $mpip3, $mpport3, $$c1);
|
||||
}
|
||||
print("switching to new port...\n");
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c2, $mpip5, $mpport5, $$c1);
|
||||
}
|
||||
|
||||
print("$dst tells $src: send RTP to $$p2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip6, $mpport6) = sim_lk($callid, $local_ip, $$p2, $ft, $tt);
|
||||
print("mediaproxy: tell $src to send to $mpport6 instead of $$p2\n");
|
||||
brk();
|
||||
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c2, $mpip5, $mpport5, $$c1);
|
||||
}
|
||||
|
||||
send_rcv4($$c2, $mpip5, $mpport5, $$c1, $mpip6, $mpport6);
|
||||
|
||||
brk();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$sub->();
|
||||
print("\n\n\n");
|
||||
print("re-invite coming from $src with intermediate traffic from $src only to both old and new ports\n");
|
||||
|
||||
print("$src tells $dst: send RTP to $$p1\n");
|
||||
brk();
|
||||
|
||||
my ($mpip7, $mpport7) = sim_rq($callid, $local_ip, $$p1, $ft);
|
||||
print("mediaproxy: tell $dst to send to $mpport7 instead of $$p1\n");
|
||||
brk();
|
||||
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c1, $mpip6, $mpport6, $$c2);
|
||||
}
|
||||
|
||||
print("$dst tells $src: send RTP to $$p2\n");
|
||||
brk();
|
||||
|
||||
my ($mpip8, $mpport8) = sim_lk($callid, $local_ip, $$p2, $ft, $tt);
|
||||
print("mediaproxy: tell $src to send to $mpport8 instead of $$p2\n");
|
||||
brk();
|
||||
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c1, $mpip6, $mpport6, $$c2);
|
||||
}
|
||||
print("switching to new port...\n");
|
||||
for (1 .. 4) {
|
||||
send_rcv_brk($$c1, $mpip8, $mpport8, $$c2);
|
||||
}
|
||||
|
||||
send_rcv4($$c2, $mpip7, $mpport7, $$c1, $mpip8, $mpport8);
|
||||
|
||||
brk();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
# # G_SLICE=always-malloc valgrind --leak-check=full --track-origins=yes --show-possibly-lost=yes ./mediaproxy-ng -t 0 -i $IP -l 25060 -f
|
||||
|
||||
pipe_o() {
|
||||
nc localhost 25060
|
||||
}
|
||||
pipe() {
|
||||
pipe_o > /dev/null
|
||||
}
|
||||
ip() {
|
||||
echo $(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1))
|
||||
}
|
||||
port() {
|
||||
echo $(($RANDOM % 64000 + 1024))
|
||||
}
|
||||
|
||||
ids=""
|
||||
ports=""
|
||||
for i in $(seq 1 1000); do
|
||||
callid=`uuid`
|
||||
test -z "$callid" && exit 1
|
||||
src=`ip`:`port`
|
||||
dst=`ip`:`port`
|
||||
gw=`ip`
|
||||
fromtag=`uuid`
|
||||
totag=`uuid`
|
||||
|
||||
src_rel=`echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o`
|
||||
dst_rel=`echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o`
|
||||
echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe
|
||||
echo version | pipe
|
||||
echo status | pipe
|
||||
|
||||
src_path=${src_rel/ //}
|
||||
dst_path=${dst_rel/ //}
|
||||
ports="$ports $src_path $dst_path"
|
||||
for port in $ports; do
|
||||
echo foobar > /dev/udp/$port
|
||||
done
|
||||
|
||||
ids="$ids $callid"
|
||||
done
|
||||
|
||||
sleep 10
|
||||
|
||||
for id in $ids; do
|
||||
echo "delete $id info=" | pipe
|
||||
done
|
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
if test "$1" != magic; then
|
||||
echo
|
||||
echo "Don't run this script manually, instead run:"
|
||||
echo " make patch KERNEL=/path/to/kernel/sources"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$3"; then
|
||||
echo
|
||||
echo "Usage:"
|
||||
echo " make patch KERNEL=/path/to/kernel/sources"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KERN=$3
|
||||
|
||||
for x in . include/linux/netfilter/ net/netfilter/Kconfig net/netfilter/Makefile; do
|
||||
if ! test -e "$KERN"/"$x"; then
|
||||
echo "I don't recognize $KERN as a kernel source tree"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
set -e
|
||||
|
||||
cp -v xt_MEDIAPROXY.h "$KERN"/include/linux/netfilter/
|
||||
cp -v xt_MEDIAPROXY.c "$KERN"/net/netfilter/
|
||||
|
||||
if ! grep -q CONFIG_NETFILTER_XT_TARGET_MEDIAPROXY "$KERN"/net/netfilter/Makefile; then
|
||||
(
|
||||
echo
|
||||
echo "EXTRA_CFLAGS += -DMEDIAPROXY_VERSION=\"\\\"$4\\\"\""
|
||||
echo 'obj-$(CONFIG_NETFILTER_XT_TARGET_MEDIAPROXY) += xt_MEDIAPROXY.o'
|
||||
) >> "$KERN"/net/netfilter/Makefile
|
||||
fi
|
||||
|
||||
if ! grep -q Kconfig\\.mediaproxy-ng "$KERN"/net/netfilter/Kconfig; then
|
||||
cat >> "$KERN"/net/netfilter/Kconfig.mediaproxy-ng << \__EOF
|
||||
config NETFILTER_XT_TARGET_MEDIAPROXY
|
||||
tristate "Sipwise NGCP MEDIAPROXY target support"
|
||||
depends on IP_NF_FILTER
|
||||
help
|
||||
Sipwise NGCP mediaproxy-ng kernel support
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
__EOF
|
||||
echo 'source "net/netfilter/Kconfig.mediaproxy-ng"' >> "$KERN"/net/netfilter/Kconfig
|
||||
fi
|
Loading…
Reference in new issue