| @ -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 | |||
| @ -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)" | |||
| @ -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 | |||