18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <stdio.h>
78c2ecf20Sopenharmony_ci#include <stdlib.h>
88c2ecf20Sopenharmony_ci#include <unistd.h>
98c2ecf20Sopenharmony_ci#include <errno.h>
108c2ecf20Sopenharmony_ci#include <fcntl.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <termios.h>
138c2ecf20Sopenharmony_ci#include <sys/wait.h>
148c2ecf20Sopenharmony_ci#include <net_user.h>
158c2ecf20Sopenharmony_ci#include <os.h>
168c2ecf20Sopenharmony_ci#include "slip.h"
178c2ecf20Sopenharmony_ci#include <um_malloc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int slip_user_init(void *data, void *dev)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct slip_data *pri = data;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	pri->dev = dev;
248c2ecf20Sopenharmony_ci	return 0;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int set_up_tty(int fd)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int i;
308c2ecf20Sopenharmony_ci	struct termios tios;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (tcgetattr(fd, &tios) < 0) {
338c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "could not get initial terminal "
348c2ecf20Sopenharmony_ci		       "attributes\n");
358c2ecf20Sopenharmony_ci		return -1;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL;
398c2ecf20Sopenharmony_ci	tios.c_iflag = IGNBRK | IGNPAR;
408c2ecf20Sopenharmony_ci	tios.c_oflag = 0;
418c2ecf20Sopenharmony_ci	tios.c_lflag = 0;
428c2ecf20Sopenharmony_ci	for (i = 0; i < NCCS; i++)
438c2ecf20Sopenharmony_ci		tios.c_cc[i] = 0;
448c2ecf20Sopenharmony_ci	tios.c_cc[VMIN] = 1;
458c2ecf20Sopenharmony_ci	tios.c_cc[VTIME] = 0;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	cfsetospeed(&tios, B38400);
488c2ecf20Sopenharmony_ci	cfsetispeed(&tios, B38400);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
518c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "failed to set terminal attributes\n");
528c2ecf20Sopenharmony_ci		return -1;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct slip_pre_exec_data {
588c2ecf20Sopenharmony_ci	int stdin_fd;
598c2ecf20Sopenharmony_ci	int stdout_fd;
608c2ecf20Sopenharmony_ci	int close_me;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void slip_pre_exec(void *arg)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct slip_pre_exec_data *data = arg;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (data->stdin_fd >= 0)
688c2ecf20Sopenharmony_ci		dup2(data->stdin_fd, 0);
698c2ecf20Sopenharmony_ci	dup2(data->stdout_fd, 1);
708c2ecf20Sopenharmony_ci	if (data->close_me >= 0)
718c2ecf20Sopenharmony_ci		close(data->close_me);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int slip_tramp(char **argv, int fd)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct slip_pre_exec_data pe_data;
778c2ecf20Sopenharmony_ci	char *output;
788c2ecf20Sopenharmony_ci	int pid, fds[2], err, output_len;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	err = os_pipe(fds, 1, 0);
818c2ecf20Sopenharmony_ci	if (err < 0) {
828c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "slip_tramp : pipe failed, err = %d\n",
838c2ecf20Sopenharmony_ci		       -err);
848c2ecf20Sopenharmony_ci		goto out;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	err = 0;
888c2ecf20Sopenharmony_ci	pe_data.stdin_fd = fd;
898c2ecf20Sopenharmony_ci	pe_data.stdout_fd = fds[1];
908c2ecf20Sopenharmony_ci	pe_data.close_me = fds[0];
918c2ecf20Sopenharmony_ci	err = run_helper(slip_pre_exec, &pe_data, argv);
928c2ecf20Sopenharmony_ci	if (err < 0)
938c2ecf20Sopenharmony_ci		goto out_close;
948c2ecf20Sopenharmony_ci	pid = err;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	output_len = UM_KERN_PAGE_SIZE;
978c2ecf20Sopenharmony_ci	output = uml_kmalloc(output_len, UM_GFP_KERNEL);
988c2ecf20Sopenharmony_ci	if (output == NULL) {
998c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "slip_tramp : failed to allocate output "
1008c2ecf20Sopenharmony_ci		       "buffer\n");
1018c2ecf20Sopenharmony_ci		os_kill_process(pid, 1);
1028c2ecf20Sopenharmony_ci		err = -ENOMEM;
1038c2ecf20Sopenharmony_ci		goto out_close;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	close(fds[1]);
1078c2ecf20Sopenharmony_ci	read_output(fds[0], output, output_len);
1088c2ecf20Sopenharmony_ci	printk("%s", output);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	err = helper_wait(pid);
1118c2ecf20Sopenharmony_ci	close(fds[0]);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	kfree(output);
1148c2ecf20Sopenharmony_ci	return err;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciout_close:
1178c2ecf20Sopenharmony_ci	close(fds[0]);
1188c2ecf20Sopenharmony_ci	close(fds[1]);
1198c2ecf20Sopenharmony_ciout:
1208c2ecf20Sopenharmony_ci	return err;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int slip_open(void *data)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct slip_data *pri = data;
1268c2ecf20Sopenharmony_ci	char version_buf[sizeof("nnnnn\0")];
1278c2ecf20Sopenharmony_ci	char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
1288c2ecf20Sopenharmony_ci	char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf,
1298c2ecf20Sopenharmony_ci			 NULL };
1308c2ecf20Sopenharmony_ci	int sfd, mfd, err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	err = get_pty();
1338c2ecf20Sopenharmony_ci	if (err < 0) {
1348c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "slip-open : Failed to open pty, err = %d\n",
1358c2ecf20Sopenharmony_ci		       -err);
1368c2ecf20Sopenharmony_ci		goto out;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci	mfd = err;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	err = open(ptsname(mfd), O_RDWR, 0);
1418c2ecf20Sopenharmony_ci	if (err < 0) {
1428c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "Couldn't open tty for slip line, "
1438c2ecf20Sopenharmony_ci		       "err = %d\n", -err);
1448c2ecf20Sopenharmony_ci		goto out_close;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	sfd = err;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	err = set_up_tty(sfd);
1498c2ecf20Sopenharmony_ci	if (err)
1508c2ecf20Sopenharmony_ci		goto out_close2;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	pri->slave = sfd;
1538c2ecf20Sopenharmony_ci	pri->slip.pos = 0;
1548c2ecf20Sopenharmony_ci	pri->slip.esc = 0;
1558c2ecf20Sopenharmony_ci	if (pri->gate_addr != NULL) {
1568c2ecf20Sopenharmony_ci		sprintf(version_buf, "%d", UML_NET_VERSION);
1578c2ecf20Sopenharmony_ci		strcpy(gate_buf, pri->gate_addr);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		err = slip_tramp(argv, sfd);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (err < 0) {
1628c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "slip_tramp failed - err = %d\n",
1638c2ecf20Sopenharmony_ci			       -err);
1648c2ecf20Sopenharmony_ci			goto out_close2;
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci		err = os_get_ifname(pri->slave, pri->name);
1678c2ecf20Sopenharmony_ci		if (err < 0) {
1688c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "get_ifname failed, err = %d\n",
1698c2ecf20Sopenharmony_ci			       -err);
1708c2ecf20Sopenharmony_ci			goto out_close2;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		iter_addresses(pri->dev, open_addr, pri->name);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	else {
1758c2ecf20Sopenharmony_ci		err = os_set_slip(sfd);
1768c2ecf20Sopenharmony_ci		if (err < 0) {
1778c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "Failed to set slip discipline "
1788c2ecf20Sopenharmony_ci			       "encapsulation - err = %d\n", -err);
1798c2ecf20Sopenharmony_ci			goto out_close2;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	return mfd;
1838c2ecf20Sopenharmony_ciout_close2:
1848c2ecf20Sopenharmony_ci	close(sfd);
1858c2ecf20Sopenharmony_ciout_close:
1868c2ecf20Sopenharmony_ci	close(mfd);
1878c2ecf20Sopenharmony_ciout:
1888c2ecf20Sopenharmony_ci	return err;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void slip_close(int fd, void *data)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct slip_data *pri = data;
1948c2ecf20Sopenharmony_ci	char version_buf[sizeof("nnnnn\0")];
1958c2ecf20Sopenharmony_ci	char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name,
1968c2ecf20Sopenharmony_ci			 NULL };
1978c2ecf20Sopenharmony_ci	int err;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (pri->gate_addr != NULL)
2008c2ecf20Sopenharmony_ci		iter_addresses(pri->dev, close_addr, pri->name);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	sprintf(version_buf, "%d", UML_NET_VERSION);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	err = slip_tramp(argv, pri->slave);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (err != 0)
2078c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "slip_tramp failed - errno = %d\n", -err);
2088c2ecf20Sopenharmony_ci	close(fd);
2098c2ecf20Sopenharmony_ci	close(pri->slave);
2108c2ecf20Sopenharmony_ci	pri->slave = -1;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciint slip_user_read(int fd, void *buf, int len, struct slip_data *pri)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	return slip_proto_read(fd, buf, len, &pri->slip);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciint slip_user_write(int fd, void *buf, int len, struct slip_data *pri)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	return slip_proto_write(fd, buf, len, &pri->slip);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void slip_add_addr(unsigned char *addr, unsigned char *netmask,
2248c2ecf20Sopenharmony_ci			  void *data)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct slip_data *pri = data;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (pri->slave < 0)
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci	open_addr(addr, netmask, pri->name);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void slip_del_addr(unsigned char *addr, unsigned char *netmask,
2348c2ecf20Sopenharmony_ci			    void *data)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct slip_data *pri = data;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (pri->slave < 0)
2398c2ecf20Sopenharmony_ci		return;
2408c2ecf20Sopenharmony_ci	close_addr(addr, netmask, pri->name);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ciconst struct net_user_info slip_user_info = {
2448c2ecf20Sopenharmony_ci	.init		= slip_user_init,
2458c2ecf20Sopenharmony_ci	.open		= slip_open,
2468c2ecf20Sopenharmony_ci	.close	 	= slip_close,
2478c2ecf20Sopenharmony_ci	.remove	 	= NULL,
2488c2ecf20Sopenharmony_ci	.add_address	= slip_add_addr,
2498c2ecf20Sopenharmony_ci	.delete_address = slip_del_addr,
2508c2ecf20Sopenharmony_ci	.mtu		= BUF_SIZE,
2518c2ecf20Sopenharmony_ci	.max_packet	= BUF_SIZE,
2528c2ecf20Sopenharmony_ci};
253