Example #1
0
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;
}
Example #2
0
int last_forwarded_ip(t_http_header *http_headers, t_ip_addr *ip_addr) {
	char *forwarded, *search, ip_str[MAX_IP_STR_LEN + 1], *begin, *end;
	size_t len;
	int port;

	if ((forwarded = get_http_header(hs_forwarded, http_headers)) != NULL) {	
		/* Forwarded header
		 */
		begin = NULL;
		while ((forwarded = strcasestr(forwarded, "for=")) == NULL) {
			begin = forwarded;
			forwarded++;
		}

		if (begin == NULL) {
			return -1;
		}

		end = begin;
		while ((*end != '\0') && (*end != ',') && (*end != ';')) {
			end++;
		}

		if (*begin == '"') {
			begin++;
			end--;
			if (*end != '"') {
				return -1;
			}
		}

		len = end - begin;
		if (len > MAX_IP_STR_LEN) {
			return -1;
		}

		memcpy(ip_str, begin, len);
		*(ip_str + len) = '\0';
		forwarded = ip_str;
	} else if ((forwarded = get_http_header(hs_x_forwarded_for, http_headers)) != NULL) {
		/* X-Forwarded-For header
		 */
		if ((search = strrchr(forwarded, ',')) != NULL) {
			forwarded = search + 1;
			while (*forwarded == ' ') {
				forwarded++;
			}
		}
	} else {
		return -1;
	}

	if (parse_ip(forwarded, ip_addr) == -1) {
		if (parse_ip_port(forwarded, ip_addr, &port) == -1) {
			return -1;
		}
	}

	return 0;
}
Example #3
0
static void options(int *argc, char ***argv) {
	char **if_a = NULL;
	char **iter;
	struct interface_address *ifa;
	char *listenps = NULL;
	char *listenudps = NULL;
	char *listenngs = NULL;
	char *redisps = NULL;
	char *log_facility_s = NULL;
	int version = 0;
	int sip_source = 0;

	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 },
		{ "interface",	'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a,	"Local interface for RTP",	"[NAME/]IP[!IP]"},
		{ "listen-tcp",	'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"	},
		{ "listen-ng",	'n', 0, G_OPTION_ARG_STRING,	&listenngs,	"UDP port to listen on, NG protocol", "[IP46:]PORT"	},
		{ "tos",	'T', 0, G_OPTION_ARG_INT,	&tos,		"Default 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_FILENAME,	&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"	},
		{ "log-level",	'L', 0, G_OPTION_ARG_INT,	(void *)&log_level,"Mask log priorities above this level","INT"	},
		{ "log-facility",0,  0,	G_OPTION_ARG_STRING, &log_facility_s, "Syslog facility to use for logging", "daemon|local0|...|local7"},
		{ "log-stderr",	'E', 0, G_OPTION_ARG_NONE,	&_log_stderr,	"Log on stderr instead of syslog",	NULL		},
		{ "xmlrpc-format",'x', 0, G_OPTION_ARG_INT,	&xmlrpc_fmt,	"XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only",	"INT"	},
		{ "num-threads",  0, 0, G_OPTION_ARG_INT,	&num_threads,	"Number of worker threads to create",	"INT"	},
		{ "sip-source",  0,  0, G_OPTION_ARG_NONE,	&sip_source,	"Use SIP source address by default",	NULL	},
		{ "dtls-passive", 0, 0, G_OPTION_ARG_NONE,	&dtls_passive_def,"Always prefer DTLS passive role",	NULL	},
		{ 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", er->message);

	if (version)
		die("%s", RTPENGINE_VERSION);

	if (!if_a)
		die("Missing option --interface");
	if (!listenps && !listenudps && !listenngs)
		die("Missing option --listen-tcp, --listen-udp or --listen-ng");

	for (iter = if_a; *iter; iter++) {
		ifa = if_addr_parse(*iter);
		if (!ifa)
			die("Invalid interface specification: %s", *iter);
		g_queue_push_tail(&interfaces, ifa);
	}

	if (listenps) {
		if (parse_ip_port(&listenp, &listenport, listenps))
			die("Invalid IP or port (--listen-tcp)");
	}
	if (listenudps) {
		if (parse_ip6_port(&udp_listenp, &udp_listenport, listenudps))
			die("Invalid IP or port (--listen-udp)");
	}
	if (listenngs) {
		if (parse_ip6_port(&ng_listenp, &ng_listenport, listenngs))
			die("Invalid IP or port (--listen-ng)");
	}

	if (tos < 0 || tos > 255)
		die("Invalid TOS value");

	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)");
		if (redis_db < 0)
			die("Must specify Redis DB number (--redis-db) when using Redis");
	}
	
	if (xmlrpc_fmt > 1)
		die("Invalid XMLRPC format");

	if ((log_level < LOG_EMERG) || (log_level > LOG_DEBUG))
	        die("Invalid log level (--log_level)");
	setlogmask(LOG_UPTO(log_level));

	if (log_facility_s) {
		if (!parse_log_facility(log_facility_s, &_log_facility)) {
			print_available_log_facilities();
			die ("Invalid log facility '%s' (--log-facility)\n", log_facility_s);
		}
	}

	if (_log_stderr) {
		write_log = log_to_stderr;
		max_log_line_length = 0;
	}

	if (!sip_source)
		trust_address_def = 1;
}
Example #4
0
int main(int argc, char* argv[])
{
	welcome();
	init(argc, argv);

	ServerConfig::max_channels = conf->get_num("front.max_channels");
	ServerConfig::max_subscribers_per_channel = conf->get_num("front.max_subscribers_per_channel");
	ServerConfig::channel_buffer_size = conf->get_num("front.channel_buffer_size");
	ServerConfig::channel_timeout = conf->get_num("front.channel_timeout");
	ServerConfig::polling_timeout = conf->get_num("front.polling_timeout");
	if (ServerConfig::polling_timeout <= 0){
		log_fatal("Invalid polling_timeout!");
		exit(0);
	}
	if (ServerConfig::channel_timeout <= 0){
		ServerConfig::channel_timeout = (int)(0.5 * ServerConfig::polling_timeout);
	}

	ServerConfig::polling_idles = ServerConfig::polling_timeout / CHANNEL_CHECK_INTERVAL;
	ServerConfig::channel_idles = ServerConfig::channel_timeout / CHANNEL_CHECK_INTERVAL;

	log_info("ServerConfig::max_channels =%d", ServerConfig::max_channels);
	log_info("ServerConfig::max_subscribers_per_channel=%d", ServerConfig::max_subscribers_per_channel);
	log_info("ServerConfig::polling_timeout=%d", ServerConfig::polling_timeout);
	log_info("ServerConfig::polling_idles =%d", ServerConfig::polling_idles);
	log_info("ServerConfig::channel_buffer_size=%d", ServerConfig::channel_buffer_size);
	log_info("ServerConfig::channel_timeout=%d", ServerConfig::channel_timeout);
	log_info("ServerConfig::channel_idles =%d", ServerConfig::channel_idles);
	serv = new Server();
	ip_filter = new IpFilter();


	{
		// /pub?cname=abc&content=hi
		// content must be json encoded string without leading quote and trailing quote
		// TODO: multi_pub
		evhttp_set_cb(admin_http, "/pub", pub_handler, NULL);
		// pub raw content(not json encoded)
		evhttp_set_cb(admin_http, "/push", push_handler, NULL);
		// 分配通道, 返回通道的id和token
		// /sign?cname=abc[&expires=60]
		// wait 60 seconds to expire before any sub
		evhttp_set_cb(admin_http, "/sign", sign_handler, NULL);
		// 销毁通道
		// /close?cname=abc
		evhttp_set_cb(admin_http, "/close", close_handler, NULL);
		// 销毁通道
		// /clear?cname=abc
		evhttp_set_cb(admin_http, "/clear", clear_handler, NULL);
		// 获取通道的信息
		// /info?[cname=abc], or TODO: /info?cname=a,b,c
		evhttp_set_cb(admin_http, "/info", info_handler, NULL);
		// 判断通道是否处于被订阅状态(所有订阅者断开连接一定时间后, 通道才转为空闲状态)
		// /check?cname=abc, or TODO: /check?cname=a,b,c
		evhttp_set_cb(admin_http, "/check", check_handler, NULL);

		// 订阅通道的状态变化信息, 如创建通道(第一个订阅者连接时), 关闭通道.
		// 通过 endless chunk 返回.
		evhttp_set_cb(admin_http, "/psub", psub_handler, NULL);

		std::string admin_ip;
		int admin_port = 0;
		parse_ip_port(conf->get_str("admin.listen"), &admin_ip, &admin_port);
		struct evhttp_bound_socket *handle;
		handle = evhttp_bind_socket_with_handle(admin_http, admin_ip.c_str(), admin_port);
		if (!handle){
			log_fatal("bind admin_port %d error! %s", admin_port, strerror(errno));
			exit(0);
		}
		log_info("admin server listen on %s:%d", admin_ip.c_str(), admin_port);

		struct evconnlistener *listener = evhttp_bound_socket_get_listener(handle);
		evconnlistener_set_error_cb(listener, accept_error_cb);
		// TODO: modify libevent, add evhttp_set_accept_cb()
	}

	// init admin ip_filter
	{
		Config *cc = (Config *)conf->get("admin");
		if (cc != NULL){
			std::vector<Config *> *children = &cc->children;
			std::vector<Config *>::iterator it;
			for (it = children->begin(); it != children->end(); it++){
				if ((*it)->key != "allow"){
					continue;
				}
				const char *ip = (*it)->str();
				log_info("    allow %s", ip);
				ip_filter->add_allow(ip);
			}
		}
	}

	{
		Config *cc = (Config *)conf->get("admin");
		if (cc != NULL){
			std::vector<Config *> *children = &cc->children;
			std::vector<Config *>::iterator it;
			for (it = children->begin(); it != children->end(); it++){
				if ((*it)->key != "deny"){
					continue;
				}
				const char *ip = (*it)->str();
				log_info("    deny %s", ip);
				ip_filter->add_deny(ip);
			}
		}
	}

	{
		// /sub?cname=abc&cb=jsonp&token=&seq=123&noop=123
		evhttp_set_cb(front_http, "/sub", poll_handler, NULL);
		evhttp_set_cb(front_http, "/poll", poll_handler, NULL);
		// forever iframe
		evhttp_set_cb(front_http, "/iframe", iframe_handler, NULL);
		// http endless chunk
		evhttp_set_cb(front_http, "/stream", stream_handler, NULL);
		// /ping?cb=jsonp
		evhttp_set_cb(front_http, "/ping", ping_handler, NULL);

		std::string front_ip;
		int front_port = 0;
		parse_ip_port(conf->get_str("front.listen"), &front_ip, &front_port);
		for (int i = 0; i < MAX_BIND_PORTS; i++){
			int port = front_port + i;

			struct evhttp_bound_socket *handle;
			handle = evhttp_bind_socket_with_handle(front_http, front_ip.c_str(), port);
			if (!handle){
				log_fatal("bind front_port %d error! %s", port, strerror(errno));
				exit(0);
			}
			log_info("front server listen on %s:%d", front_ip.c_str(), port);

			struct evconnlistener *listener = evhttp_bound_socket_get_listener(handle);
			evconnlistener_set_error_cb(listener, accept_error_cb);
		}

		std::string auth = conf->get_str("front.auth");
		log_info("    auth %s", auth.c_str());
		log_info("    max_channels %d", ServerConfig::max_channels);
		log_info("    max_subscribers_per_channel %d", ServerConfig::max_subscribers_per_channel);
		log_info("    channel_buffer_size %d", ServerConfig::channel_buffer_size);
		log_info("    channel_timeout %d", ServerConfig::channel_timeout);
		log_info("    polling_timeout %d", ServerConfig::polling_timeout);
		if (auth == "token"){
			serv->auth = Server::AUTH_TOKEN;
		}
	}

	//write_pidfile();
	log_info("chatserv started");
	event_base_dispatch(evbase);
	//remove_pidfile();

	event_free(timer_event);
	event_free(sigint_event);
	event_free(sigterm_event);
	evhttp_free(admin_http);
	evhttp_free(front_http);
	event_base_free(evbase);

	delete serv;
	delete conf;
	log_info("chatserv exit");

	return 0;
}
Example #5
0
static void options(int *argc, char ***argv) {
	char *ipv4s = NULL;
	char *adv_ipv4s = NULL;
	char *ipv6s = NULL;
	char *adv_ipv6s = NULL;
	char *listenps = NULL;
	char *listenudps = NULL;
	char *listenngs = NULL;
	char *redisps = NULL;
	char *log_facility_s = NULL;
	int version = 0;

	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-tcp",	'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"	},
		{ "listen-ng",	'n', 0, G_OPTION_ARG_STRING,	&listenngs,	"UDP port to listen on, NG protocol", "[IP46:]PORT"	},
		{ "tos",	'T', 0, G_OPTION_ARG_INT,	&tos,		"Default 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"	},
		{ "log-level",	'L', 0, G_OPTION_ARG_INT,	(void *)&log_level,	"Mask log priorities above this level",	"INT"	},
		{ "log-facility",	0,	0,	G_OPTION_ARG_STRING, &log_facility_s, "Syslog facility to use for logging", "daemon|local0|...|local7"},
		{ "log-stderr",	'E', 0, G_OPTION_ARG_NONE,	&_log_stderr,	"Log on stderr instead of syslog",	NULL		},
		{ "xmlrpc-format",	'x', 0, G_OPTION_ARG_INT,	&xmlrpc_fmt,	"XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only",	"INT"	},
		{ 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", RTPENGINE_VERSION);

	if (!ipv4s)
		die("Missing option --ip\n");
	if (!listenps && !listenudps && !listenngs)
		die("Missing option --listen-tcp, --listen-udp or --listen-ng\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-tcp)\n");
	}
	if (listenudps) {
		if (parse_ip6_port(&udp_listenp, &udp_listenport, listenudps))
			die("Invalid IP or port (--listen-udp)\n");
	}
	if (listenngs) {
		if (parse_ip6_port(&ng_listenp, &ng_listenport, listenngs))
			die("Invalid IP or port (--listen-ng)\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");
	}
	
	if (xmlrpc_fmt < 0 || xmlrpc_fmt > 1) {
		die("Invalid XMLRPC format\n");
	}

	if ((log_level < LOG_EMERG) || (log_level > LOG_DEBUG))
	        die("Invalid log level (--log_level)\n");
	setlogmask(LOG_UPTO(log_level));

	if (log_facility_s) {
		if (!parse_log_facility(log_facility_s, &_log_facility)) {
			print_available_log_facilities();
			die ("Invalid log facility '%s' (--log-facility)\n", log_facility_s);
		}
	}

	if (_log_stderr) {
		write_log = log_to_stderr;
	}
}
Example #6
0
void open_socket_inet(device_dev_t* if_device, options_t *options) {

   // TODO: not yet ready !!!
   ip_port_t        srv_addr; // address to connect the probe to
   struct addrinfo* a_res;    // address results
   struct addrinfo  a_hint;   // address results

   // parse address input string '<ip>:<port>'
   srv_addr = parse_ip_port(if_device->device_name);
   LOGGER_info("INET socket: '%s:%s'", srv_addr.ip, srv_addr.port);

   // TODO: IPv4 support IPv6 is added later
   memset( &a_hint, 0, sizeof(a_hint) );
   a_hint.ai_family   = options->ai_family; //AF_INET; // AF_INET6;
   //a_hint.ai_socktype = SOCK_STREAM; //SOCK_DGRAM;
   a_hint.ai_socktype = SOCK_DGRAM; //SOCK_STREAM;
   a_hint.ai_flags    = AI_PASSIVE;  // server socket
   //a_hint.ai_flags    |= AI_ADDRCONFIG;
   //a_hint.ai_flags    |= AI_V4MAPPED;
   a_hint.ai_protocol = IPPROTO_UDP; // udp protocol

   // get all availabe address for the given ip port
   //int rv = getaddrinfo(srv_addr.ip, srv_addr.port, &a_hint, &a_res);
   int rv = getaddrinfo(NULL, srv_addr.port, &a_hint, &a_res);
   if( 0 != rv ) {
      perror( "Socket error in getaddrinfo()" );
      perror( gai_strerror(rv) );
      exit(1);
   }

   // print the return addresses
   print_addrinfo( a_res );

   if( NULL != a_res ) {
      //LOGGER_info( "connect" );
      LOGGER_info( "bind" );

      int socket = -1;

      // create a socket for the returned service
      //if_device->device_handle.socket = create_connect( a_res );
      socket = create_bind( a_res );
      if(-1 == socket ) {
         //perror("socket create_connect error");
         perror("socket create_bind error");
         exit(1);
      }

      if_device->device_handle.socket = socket;
      if_device->dh.fd = socket;
      if_device->dispatch = socket_dispatch_inet;

      // send 'hello' TODO: for test only
      //write( if_device->device_handle.socket, "HELLO!", 6 );
      //write( if_device->device_handle.socket, "", 1 );

      // TODO: some rework is still needed
      // register read handling to ev_handler
      LOGGER_debug("Register io handling for interface: %s", if_device->device_name);

      setNONBlocking(if_device);

      int fd = get_file_desc(if_device);
      LOGGER_debug("File Descriptor: %d", fd);

      /* storing a reference of packet device to
       be passed via watcher on a packet event so
       we know which device to read the packet from */
      // TODO: implement an own packet watcher callback
      LOGGER_info("register event io: read inet socket interface (%s)", if_device->device_name);
      ev_watcher* watcher = event_register_io_r(EV_DEFAULT_ packet_watcher_cb, fd);
      watcher->data = (device_dev_t *) if_device;
   }

   // TODO: for UDP send initial message ?
   return;
}