예제 #1
0
int
Mavlink::set_hil_enabled(bool hil_enabled)
{
	int ret = OK;

	/* enable HIL */
	if (hil_enabled && !_hil_enabled) {
		_hil_enabled = true;
		configure_stream("HIL_CONTROLS", 200.0f);
	}

	/* disable HIL */
	if (!hil_enabled && _hil_enabled) {
		_hil_enabled = false;
		configure_stream("HIL_CONTROLS", 0.0f);

	} else {
		ret = ERROR;
	}

	return ret;
}
예제 #2
0
int
Mavlink::set_hil_enabled(bool hil_enabled)
{
	int ret = OK;

	/* enable HIL */
	if (hil_enabled && !_hil_enabled) {
		_hil_enabled = true;
		float rate_mult = _datarate / 1000.0f;
		configure_stream("HIL_CONTROLS", 15.0f * rate_mult);
	}

	/* disable HIL */
	if (!hil_enabled && _hil_enabled) {
		_hil_enabled = false;
		configure_stream("HIL_CONTROLS", 0.0f);

	} else {
		ret = ERROR;
	}

	return ret;
}
예제 #3
0
static void
doit(int f,
	struct sockaddr_storage *fromp,
	krb5_context krb_context,
	int encr_flag,
	krb5_keytab keytab)
{
	int p, t, on = 1;
	char c;
	char abuf[INET6_ADDRSTRLEN];
	struct sockaddr_in *sin;
	struct sockaddr_in6 *sin6;
	int fromplen;
	in_port_t port;
	struct termios tp;
	boolean_t bad_port;
	boolean_t no_name;
	char rhost_addra[INET6_ADDRSTRLEN];

	if (!(rlbuf = malloc(BUFSIZ))) {
		syslog(LOG_ERR, "rlbuf malloc failed\n");
		exit(EXIT_FAILURE);
	}
	(void) alarm(60);
	if (read(f, &c, 1) != 1 || c != 0) {
		syslog(LOG_ERR, "failed to receive protocol zero byte\n");
		exit(EXIT_FAILURE);
	}
	(void) alarm(0);
	if (fromp->ss_family == AF_INET) {
		sin = (struct sockaddr_in *)fromp;
		port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
		fromplen = sizeof (struct sockaddr_in);

		if (!inet_ntop(AF_INET, &sin->sin_addr,
			    rhost_addra, sizeof (rhost_addra)))
			goto badconversion;
	} else if (fromp->ss_family == AF_INET6) {
		sin6 = (struct sockaddr_in6 *)fromp;
		port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
		fromplen = sizeof (struct sockaddr_in6);

		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
			struct in_addr ipv4_addr;

			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
					    &ipv4_addr);
			if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
				    sizeof (rhost_addra)))
				goto badconversion;
		} else {
			if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
				    rhost_addra, sizeof (rhost_addra)))
				goto badconversion;
		}
	} else {
		syslog(LOG_ERR, "unknown address family %d\n",
		    fromp->ss_family);
		fatal(f, "Permission denied");
	}

	/*
	 * Allow connections only from the "ephemeral" reserved
	 * ports(ports 512 - 1023) by checking the remote port
	 * because other utilities(e.g. in.ftpd) can be used to
	 * allow a unprivileged user to originate a connection
	 * from a privileged port and provide untrustworthy
	 * authentication.
	 */
	bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
		    (port >= (in_port_t)IPPORT_RESERVED) ||
		    (port < (in_port_t)(IPPORT_RESERVED/2)));
	no_name = getnameinfo((const struct sockaddr *) fromp,
			    fromplen, hostname, sizeof (hostname),
			    NULL, 0, 0) != 0;

	if (no_name || bad_port) {
		(void) strlcpy(abuf, rhost_addra, sizeof (abuf));
		/* If no host name, use IP address for name later on. */
		if (no_name)
			(void) strlcpy(hostname, abuf, sizeof (hostname));
	}

	if (!no_name) {
		/*
		 * Even if getnameinfo() succeeded, we still have to check
		 * for spoofing.
		 */
		check_address("rlogind", fromp, sin, sin6, rhost_addra,
		    hostname, sizeof (hostname));
	}

	if (bad_port) {
		if (no_name)
			syslog(LOG_NOTICE,
			    "connection from %s - bad port\n",
			    abuf);
		else
			syslog(LOG_NOTICE,
			    "connection from %s(%s) - bad port\n",
			    hostname, abuf);
		fatal(f, "Permission denied");
	}

	if (use_auth == KRB5_RECVAUTH_V5) {
		do_krb_login(f, rhost_addra, hostname,
			    krb_context, encr_flag, keytab);
		if (krusername != NULL && strlen(krusername)) {
			/*
			 * Kerberos Authentication succeeded,
			 * so set the proper program name to use
			 * with pam (important during 'cleanup'
			 * routine later).
			 */
			pam_prog_name = KRB5_PROG_NAME;
		}
	}

	if (write(f, "", 1) != 1) {
		syslog(LOG_NOTICE,
		    "send of the zero byte(to %s) failed:"
		    " cannot start data transfer mode\n",
		    (no_name ? abuf : hostname));
		exit(EXIT_FAILURE);
	}
	if ((p = open("/dev/ptmx", O_RDWR)) == -1)
		fatalperror(f, "cannot open /dev/ptmx");
	if (grantpt(p) == -1)
		fatal(f, "could not grant slave pty");
	if (unlockpt(p) == -1)
		fatal(f, "could not unlock slave pty");
	if ((line = ptsname(p)) == NULL)
		fatal(f, "could not enable slave pty");
	if ((t = open(line, O_RDWR)) == -1)
		fatal(f, "could not open slave pty");
	if (ioctl(t, I_PUSH, "ptem") == -1)
		fatalperror(f, "ioctl I_PUSH ptem");
	if (ioctl(t, I_PUSH, "ldterm") == -1)
		fatalperror(f, "ioctl I_PUSH ldterm");
	if (ioctl(t, I_PUSH, "ttcompat") == -1)
		fatalperror(f, "ioctl I_PUSH ttcompat");
	/*
	 * POP the sockmod and push the rlmod module.
	 *
	 * Note that sockmod has to be removed since readstream assumes
	 * a "raw" TPI endpoint(e.g. it uses getmsg).
	 */
	if (removemod(f, "sockmod") < 0)
		fatalperror(f, "couldn't remove sockmod");

	if (encr_flag) {
		if (ioctl(f, I_PUSH, "cryptmod") < 0)
		    fatalperror(f, "ioctl I_PUSH rlmod");

	}

	if (ioctl(f, I_PUSH, "rlmod") < 0)
		fatalperror(f, "ioctl I_PUSH rlmod");

	if (encr_flag) {
		/*
		 * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
		 */
		uchar_t passthru = 1;
		struct strioctl rlmodctl;

		rlmodctl.ic_cmd = CRYPTPASSTHRU;
		rlmodctl.ic_timout = -1;
		rlmodctl.ic_len = sizeof (uchar_t);
		rlmodctl.ic_dp = (char *)&passthru;

		if (ioctl(f, I_STR, &rlmodctl) < 0)
			fatal(f, "ioctl CRYPTPASSTHRU failed\n");
	}

	/*
	 * readstream will do a getmsg till it receives
	 * M_PROTO type T_DATA_REQ from rloginmodopen()
	 * indicating all data on the stream prior to pushing rlmod has
	 * been drained at the stream head.
	 */
	if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
		fatalperror(f, "readstream failed");
	/*
	 * Make sure the pty doesn't modify the strings passed
	 * to login as part of the "rlogin protocol."  The login
	 * program should set these flags to apropriate values
	 * after it has read the strings.
	 */
	if (ioctl(t, TCGETS, &tp) == -1)
		fatalperror(f, "ioctl TCGETS");
	tp.c_lflag &= ~(ECHO|ICANON);
	tp.c_oflag &= ~(XTABS|OCRNL);
	tp.c_iflag &= ~(IGNPAR|ICRNL);
	if (ioctl(t, TCSETS, &tp) == -1)
		fatalperror(f, "ioctl TCSETS");

	/*
	 * System V ptys allow the TIOC{SG}WINSZ ioctl to be
	 * issued on the master side of the pty.  Luckily, that's
	 * the only tty ioctl we need to do do, so we can close the
	 * slave side in the parent process after the fork.
	 */
	(void) ioctl(p, TIOCSWINSZ, &win);

	pid = fork();
	if (pid < 0)
		fatalperror(f, "fork");
	if (pid == 0) {
		int tt;
		struct utmpx ut;

		/* System V login expects a utmp entry to already be there */
		(void) memset(&ut, 0, sizeof (ut));
		(void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
		ut.ut_pid = getpid();
		ut.ut_id[0] = 'r';
		ut.ut_id[1] = (char)SC_WILDC;
		ut.ut_id[2] = (char)SC_WILDC;
		ut.ut_id[3] = (char)SC_WILDC;
		ut.ut_type = LOGIN_PROCESS;
		ut.ut_exit.e_termination = 0;
		ut.ut_exit.e_exit = 0;
		(void) time(&ut.ut_tv.tv_sec);
		if (makeutx(&ut) == NULL)
			syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");

		/* controlling tty */
		if (setsid() == -1)
			fatalperror(f, "setsid");
		if ((tt = open(line, O_RDWR)) == -1)
			fatalperror(f, "could not re-open slave pty");

		if (close(p) == -1)
			fatalperror(f, "error closing pty master");
		if (close(t) == -1)
			fatalperror(f, "error closing pty slave"
				    " opened before session established");
		/*
		 * If this fails we may or may not be able to output an
		 * error message.
		 */
		if (close(f) == -1)
			fatalperror(f, "error closing deamon stdout");
		if (dup2(tt, STDIN_FILENO) == -1 ||
		    dup2(tt, STDOUT_FILENO) == -1 ||
		    dup2(tt, STDERR_FILENO) == -1)
			exit(EXIT_FAILURE);	/* Disaster! No stderr! */

		(void) close(tt);

		if (use_auth == KRB5_RECVAUTH_V5 &&
		    krusername != NULL && strlen(krusername)) {
			(void) execl(LOGIN_PROGRAM, "login",
				    "-d", line,
				    "-r", hostname,
				    "-u", krusername, /* KRB5 principal name */
				    "-s", pam_prog_name,
				    "-t", term,	/* Remote Terminal */
				    "-U", rusername,	/* Remote User */
				    "-R", KRB5_REPOSITORY_NAME,
				    lusername,  /* local user */
				    NULL);
		} else {
			(void) execl(LOGIN_PROGRAM, "login",
				"-d", line,
				"-r", hostname,
				NULL);
		}

		fatalperror(STDERR_FILENO, "/bin/login");
		/*NOTREACHED*/
	}
	(void) close(t);
	(void) ioctl(f, FIONBIO, &on);
	(void) ioctl(p, FIONBIO, &on);

	/*
	 * Must ignore SIGTTOU, otherwise we'll stop
	 * when we try and set slave pty's window shape
	 * (our controlling tty is the master pty).
	 * Likewise, we don't want any of the tty-generated
	 * signals from chars passing through.
	 */
	(void) sigset(SIGTSTP, SIG_IGN);
	(void) sigset(SIGINT, SIG_IGN);
	(void) sigset(SIGQUIT, SIG_IGN);
	(void) sigset(SIGTTOU, SIG_IGN);
	(void) sigset(SIGTTIN, SIG_IGN);
	(void) sigset(SIGCHLD, cleanup);
	(void) setpgrp();

	if (encr_flag) {
		krb5_data ivec, *ivptr;
		uint_t ivec_usage;
		stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);

		/*
		 * Configure the STREAMS crypto module.  For now,
		 * don't use any IV parameter.  KCMDV0.2 support
		 * will require the use of Initialization Vectors
		 * for both encrypt and decrypt modes.
		 */
		if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
			if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
				/*
				 * This is gross but necessary for MIT compat.
				 */
				ivec.length = session_key->length;
				ivec.data = (char *)session_key->contents;
				ivec_usage = IVEC_REUSE;
				ivptr = &ivec;
			} else {
				ivptr = NULL; /* defaults to all 0's */
				ivec_usage = IVEC_NEVER;
			}
			/*
			 * configure both sides of stream together
			 * since they share the same IV.
			 * This is what makes the OLD KCMD protocol
			 * less secure than the newer one - Bad ivecs.
			 */
			if (configure_stream(f, session_key,
				CRYPT_ENCRYPT|CRYPT_DECRYPT,
				ivptr, ivec_usage) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");
		} else {
			size_t blocksize;
			if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
			    session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
				if (configure_stream(f, session_key,
					CRYPT_ENCRYPT|CRYPT_DECRYPT,
					NULL, IVEC_NEVER) != 0)
					fatal(f,
					"Cannot initialize encryption -"
					" exiting.\n");
				goto startcrypto;
			}
			if (krb5_c_block_size(krb_context,
					    session_key->enctype,
					    &blocksize)) {
				syslog(LOG_ERR, "Cannot determine blocksize "
				    "for encryption type %d",
				    session_key->enctype);
				fatal(f, "Cannot determine blocksize "
				    "for encryption - exiting.\n");
			}
			ivec.data = (char *)malloc(blocksize);
			ivec.length = blocksize;
			if (ivec.data == NULL)
				fatal(f, "memory error - exiting\n");
			/*
			 * Following MIT convention -
			 *   encrypt IV = 0x01 x blocksize
			 *   decrypt IV = 0x00 x blocksize
			 *   ivec_usage = IVEC_ONETIME
			 *
			 * configure_stream separately for encrypt and
			 * decrypt because there are 2 different IVs.
			 *
			 * AES uses 0's for IV.
			 */
			if (session_key->enctype ==
				ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
			    session_key->enctype ==
				ENCTYPE_AES256_CTS_HMAC_SHA1_96)
				(void) memset(ivec.data, 0x00, blocksize);
			else
				(void) memset(ivec.data, 0x01, blocksize);
			if (configure_stream(f, session_key, CRYPT_ENCRYPT,
				&ivec, IVEC_ONETIME) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");
			(void) memset(ivec.data, 0x00, blocksize);
			if (configure_stream(f, session_key, CRYPT_DECRYPT,
				&ivec, IVEC_ONETIME) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");

			(void) free(ivec.data);
		}
startcrypto:
		start_stream(f, CRYPT_ENCRYPT);
		start_stream(f, CRYPT_DECRYPT);
	}
	protocol(f, p, encr_flag);
	cleanup(0);
	/*NOTREACHED*/

badconversion:
	fatalperror(f, "address conversion");
	/*NOTREACHED*/
}
예제 #4
0
int
Mavlink::task_main(int argc, char *argv[])
{
	int ch;
	_baudrate = 57600;
	_datarate = 0;
	_mode = MAVLINK_MODE_NORMAL;

	/* work around some stupidity in task_create's argv handling */
	argc -= 2;
	argv += 2;

	/* don't exit from getopt loop to leave getopt global variables in consistent state,
	 * set error flag instead */
	bool err_flag = false;

	while ((ch = getopt(argc, argv, "b:r:d:m:fpvwx")) != EOF) {
		switch (ch) {
		case 'b':
			_baudrate = strtoul(optarg, NULL, 10);

			if (_baudrate < 9600 || _baudrate > 921600) {
				warnx("invalid baud rate '%s'", optarg);
				err_flag = true;
			}

			break;

		case 'r':
			_datarate = strtoul(optarg, NULL, 10);

			if (_datarate < 10 || _datarate > MAX_DATA_RATE) {
				warnx("invalid data rate '%s'", optarg);
				err_flag = true;
			}

			break;

		case 'd':
			_device_name = optarg;
			break;

//		case 'e':
//			mavlink_link_termination_allowed = true;
//			break;

		case 'm':
			if (strcmp(optarg, "custom") == 0) {
				_mode = MAVLINK_MODE_CUSTOM;

			} else if (strcmp(optarg, "camera") == 0) {
				_mode = MAVLINK_MODE_CAMERA;
			}

			break;

		case 'f':
			_forwarding_on = true;
			break;

		case 'p':
			_passing_on = true;
			break;

		case 'v':
			_verbose = true;
			break;

		case 'w':
			_wait_to_transmit = true;
			break;

		case 'x':
			_ftp_on = true;
			break;

		default:
			err_flag = true;
			break;
		}
	}

	if (err_flag) {
		usage();
		return ERROR;
	}

	if (_datarate == 0) {
		/* convert bits to bytes and use 1/2 of bandwidth by default */
		_datarate = _baudrate / 20;
	}

	if (_datarate > MAX_DATA_RATE) {
		_datarate = MAX_DATA_RATE;
	}

	if (Mavlink::instance_exists(_device_name, this)) {
		warnx("mavlink instance for %s already running", _device_name);
		return ERROR;
	}

	/* inform about mode */
	switch (_mode) {
	case MAVLINK_MODE_NORMAL:
		warnx("mode: NORMAL");
		break;

	case MAVLINK_MODE_CUSTOM:
		warnx("mode: CUSTOM");
		break;

	case MAVLINK_MODE_CAMERA:
		warnx("mode: CAMERA");
		break;

	default:
		warnx("ERROR: Unknown mode");
		break;
	}

	warnx("data rate: %d Bytes/s, port: %s, baud: %d", _datarate, _device_name, _baudrate);

	/* flush stdout in case MAVLink is about to take it over */
	fflush(stdout);

	struct termios uart_config_original;

	/* default values for arguments */
	_uart_fd = mavlink_open_uart(_baudrate, _device_name, &uart_config_original, &_is_usb_uart);

	if (_uart_fd < 0) {
		warn("could not open %s", _device_name);
		return ERROR;
	}

	/* initialize mavlink text message buffering */
	mavlink_logbuffer_init(&_logbuffer, 5);

	/* if we are passing on mavlink messages, we need to prepare a buffer for this instance */
	if (_passing_on || _ftp_on) {
		/* initialize message buffer if multiplexing is on or its needed for FTP.
		 * make space for two messages plus off-by-one space as we use the empty element
		 * marker ring buffer approach.
		 */
		if (OK != message_buffer_init(2 * MAVLINK_MAX_PACKET_LEN + 2)) {
			errx(1, "can't allocate message buffer, exiting");
		}

		/* initialize message buffer mutex */
		pthread_mutex_init(&_message_buffer_mutex, NULL);
	}

	/* create the device node that's used for sending text log messages, etc. */
	register_driver(MAVLINK_LOG_DEVICE, &fops, 0666, NULL);

	/* initialize logging device */
	_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);

	/* Initialize system properties */
	mavlink_update_system();

	/* start the MAVLink receiver */
	_receive_thread = MavlinkReceiver::receive_start(this);

	_mission_result_sub = orb_subscribe(ORB_ID(mission_result));

	/* create mission manager */
	_mission_manager = new MavlinkMissionManager(this);
	_mission_manager->set_verbose(_verbose);

	_task_running = true;

	MavlinkOrbSubscription *param_sub = add_orb_subscription(ORB_ID(parameter_update));
	uint64_t param_time = 0;
	MavlinkOrbSubscription *status_sub = add_orb_subscription(ORB_ID(vehicle_status));
	uint64_t status_time = 0;

	struct vehicle_status_s status;
	status_sub->update(&status_time, &status);

	MavlinkCommandsStream commands_stream(this, _channel);

	/* add default streams depending on mode and intervals depending on datarate */
	float rate_mult = get_rate_mult();

	configure_stream("HEARTBEAT", 1.0f);

	switch (_mode) {
	case MAVLINK_MODE_NORMAL:
		configure_stream("SYS_STATUS", 1.0f);
		configure_stream("GPS_GLOBAL_ORIGIN", 0.5f);
		configure_stream("HIGHRES_IMU", 1.0f * rate_mult);
		configure_stream("ATTITUDE", 10.0f * rate_mult);
		configure_stream("VFR_HUD", 8.0f * rate_mult);
		configure_stream("GPS_RAW_INT", 1.0f * rate_mult);
		configure_stream("GLOBAL_POSITION_INT", 3.0f * rate_mult);
		configure_stream("LOCAL_POSITION_NED", 3.0f * rate_mult);
		configure_stream("RC_CHANNELS_RAW", 1.0f * rate_mult);
		configure_stream("NAMED_VALUE_FLOAT", 1.0f * rate_mult);
		configure_stream("GLOBAL_POSITION_SETPOINT_INT", 3.0f * rate_mult);
		configure_stream("ROLL_PITCH_YAW_THRUST_SETPOINT", 3.0f * rate_mult);
		configure_stream("DISTANCE_SENSOR", 0.5f);
		break;

	case MAVLINK_MODE_CAMERA:
		configure_stream("SYS_STATUS", 1.0f);
		configure_stream("ATTITUDE", 15.0f * rate_mult);
		configure_stream("GLOBAL_POSITION_INT", 15.0f * rate_mult);
		configure_stream("CAMERA_CAPTURE", 1.0f);
		break;

	default:
		break;
	}

	/* don't send parameters on startup without request */
	_mavlink_param_queue_index = param_count();

	MavlinkRateLimiter fast_rate_limiter(30000.0f / rate_mult);

	/* set main loop delay depending on data rate to minimize CPU overhead */
	_main_loop_delay = MAIN_LOOP_DELAY / rate_mult;

	/* now the instance is fully initialized and we can bump the instance count */
	LL_APPEND(_mavlink_instances, this);

	while (!_task_should_exit) {
		/* main loop */
		usleep(_main_loop_delay);

		perf_begin(_loop_perf);

		hrt_abstime t = hrt_absolute_time();

		if (param_sub->update(&param_time, nullptr)) {
			/* parameters updated */
			mavlink_update_system();
		}

		if (status_sub->update(&status_time, &status)) {
			/* switch HIL mode if required */
			set_hil_enabled(status.hil_state == HIL_STATE_ON);
		}

		/* update commands stream */
		commands_stream.update(t);

		/* check for requested subscriptions */
		if (_subscribe_to_stream != nullptr) {
			if (OK == configure_stream(_subscribe_to_stream, _subscribe_to_stream_rate)) {
				if (_subscribe_to_stream_rate > 0.0f) {
					warnx("stream %s on device %s enabled with rate %.1f Hz", _subscribe_to_stream, _device_name,
					      (double)_subscribe_to_stream_rate);

				} else {
					warnx("stream %s on device %s disabled", _subscribe_to_stream, _device_name);
				}

			} else {
				warnx("stream %s on device %s not found", _subscribe_to_stream, _device_name);
			}

			delete _subscribe_to_stream;
			_subscribe_to_stream = nullptr;
		}

		/* update streams */
		MavlinkStream *stream;
		LL_FOREACH(_streams, stream) {
			stream->update(t);
		}

		if (fast_rate_limiter.check(t)) {
			mavlink_pm_queued_send();
			_mission_manager->eventloop();

			if (!mavlink_logbuffer_is_empty(&_logbuffer)) {
				struct mavlink_logmessage msg;
				int lb_ret = mavlink_logbuffer_read(&_logbuffer, &msg);

				if (lb_ret == OK) {
					send_statustext(msg.severity, msg.text);
				}
			}
		}

		/* pass messages from other UARTs or FTP worker */
		if (_passing_on || _ftp_on) {

			bool is_part;
			uint8_t *read_ptr;
			uint8_t *write_ptr;

			pthread_mutex_lock(&_message_buffer_mutex);
			int available = message_buffer_get_ptr((void **)&read_ptr, &is_part);
			pthread_mutex_unlock(&_message_buffer_mutex);

			if (available > 0) {
				// Reconstruct message from buffer

				mavlink_message_t msg;
				write_ptr = (uint8_t *)&msg;

				// Pull a single message from the buffer
				size_t read_count = available;

				if (read_count > sizeof(mavlink_message_t)) {
					read_count = sizeof(mavlink_message_t);
				}

				memcpy(write_ptr, read_ptr, read_count);

				// We hold the mutex until after we complete the second part of the buffer. If we don't
				// we may end up breaking the empty slot overflow detection semantics when we mark the
				// possibly partial read below.
				pthread_mutex_lock(&_message_buffer_mutex);

				message_buffer_mark_read(read_count);

				/* write second part of buffer if there is some */
				if (is_part && read_count < sizeof(mavlink_message_t)) {
					write_ptr += read_count;
					available = message_buffer_get_ptr((void **)&read_ptr, &is_part);
					read_count = sizeof(mavlink_message_t) - read_count;
					memcpy(write_ptr, read_ptr, read_count);
					message_buffer_mark_read(available);
				}

				pthread_mutex_unlock(&_message_buffer_mutex);

				_mavlink_resend_uart(_channel, &msg);
			}
		}

		/* update TX/RX rates*/
		if (t > _bytes_timestamp + 1000000) {
			if (_bytes_timestamp != 0) {
				float dt = (t - _bytes_timestamp) / 1000.0f;
				_rate_tx = _bytes_tx / dt;
				_rate_txerr = _bytes_txerr / dt;
				_rate_rx = _bytes_rx / dt;
				_bytes_tx = 0;
				_bytes_txerr = 0;
				_bytes_rx = 0;
			}
			_bytes_timestamp = t;
		}

		perf_end(_loop_perf);
	}