/* * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL. */ #include #include #include #include #include #include #include #include #include "kern_constants.h" #include "net_user.h" #include "os.h" #include "slip.h" #include "um_malloc.h" #include "user.h" static int slip_user_init(void *data, void *dev) { struct slip_data *pri = data; pri->dev = dev; return 0; } static int set_up_tty(int fd) { int i; struct termios tios; if (tcgetattr(fd, &tios) < 0) { printk(UM_KERN_ERR "could not get initial terminal " "attributes\n"); return -1; } tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; tios.c_iflag = IGNBRK | IGNPAR; tios.c_oflag = 0; tios.c_lflag = 0; for (i = 0; i < NCCS; i++) tios.c_cc[i] = 0; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; cfsetospeed(&tios, B38400); cfsetispeed(&tios, B38400); if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { printk(UM_KERN_ERR "failed to set terminal attributes\n"); return -1; } return 0; } struct slip_pre_exec_data { int stdin; int stdout; int close_me; }; static void slip_pre_exec(void *arg) { struct slip_pre_exec_data *data = arg; if (data->stdin >= 0) dup2(data->stdin, 0); dup2(data->stdout, 1); if (data->close_me >= 0) close(data->close_me); } static int slip_tramp(char **argv, int fd) { struct slip_pre_exec_data pe_data; char *output; int pid, fds[2], err, output_len; err = os_pipe(fds, 1, 0); if (err < 0) { printk(UM_KERN_ERR "slip_tramp : pipe failed, err = %d\n", -err); goto out; } err = 0; pe_data.stdin = fd; pe_data.stdout = fds[1]; pe_data.close_me = fds[0]; err = run_helper(slip_pre_exec, &pe_data, argv); if (err < 0) goto out_close; pid = err; output_len = UM_KERN_PAGE_SIZE; output = kmalloc(output_len, UM_GFP_KERNEL); if (output == NULL) { printk(UM_KERN_ERR "slip_tramp : failed to allocate output " "buffer\n"); os_kill_process(pid, 1); err = -ENOMEM; goto out_free; } close(fds[1]); read_output(fds[0], output, output_len); printk("%s", output); err = helper_wait(pid); close(fds[0]); out_free: kfree(output); return err; out_close: close(fds[0]); close(fds[1]); out: return err; } static int slip_open(void *data) { struct slip_data *pri = data; char version_buf[sizeof("nnnnn\0")]; char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, NULL }; int sfd, mfd, err; err = get_pty(); if (err < 0) { printk(UM_KERN_ERR "slip-open : Failed to open pty, err = %d\n", -err); goto out; } mfd = err; err = open(ptsname(mfd), O_RDWR, 0); if (err < 0) { printk(UM_KERN_ERR "Couldn't open tty for slip line, " "err = %d\n", -err); goto out_close; } sfd = err; if (set_up_tty(sfd)) goto out_close2; pri->slave = sfd; pri->slip.pos = 0; pri->slip.esc = 0; if (pri->gate_addr != NULL) { sprintf(version_buf, "%d", UML_NET_VERSION); strcpy(gate_buf, pri->gate_addr); err = slip_tramp(argv, sfd); if (err < 0) { printk(UM_KERN_ERR "slip_tramp failed - err = %d\n", -err); goto out_close2; } err = os_get_ifname(pri->slave, pri->name); if (err < 0) { printk(UM_KERN_ERR "get_ifname failed, err = %d\n", -err); goto out_close2; } iter_addresses(pri->dev, open_addr, pri->name); } else { err = os_set_slip(sfd); if (err < 0) { printk(UM_KERN_ERR "Failed to set slip discipline " "encapsulation - err = %d\n", -err); goto out_close2; } } return mfd; out_close2: close(sfd); out_close: close(mfd); out: return err; } static void slip_close(int fd, void *data) { struct slip_data *pri = data; char version_buf[sizeof("nnnnn\0")]; char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, NULL }; int err; if (pri->gate_addr != NULL) iter_addresses(pri->dev, close_addr, pri->name); sprintf(version_buf, "%d", UML_NET_VERSION); err = slip_tramp(argv, pri->slave); if (err != 0) printk(UM_KERN_ERR "slip_tramp failed - errno = %d\n", -err); close(fd); close(pri->slave); pri->slave = -1; } int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) { return slip_proto_read(fd, buf, len, &pri->slip); } int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) { return slip_proto_write(fd, buf, len, &pri->slip); } static void slip_add_addr(unsigned char *addr, unsigned char *netmask, void *data) { struct slip_data *pri = data; if (pri->slave < 0) return; open_addr(addr, netmask, pri->name); } static void slip_del_addr(unsigned char *addr, unsigned char *netmask, void *data) { struct slip_data *pri = data; if (pri->slave < 0) return; close_addr(addr, netmask, pri->name); } const struct net_user_info slip_user_info = { .init = slip_user_init, .open = slip_open, .close = slip_close, .remove = NULL, .add_address = slip_add_addr, .delete_address = slip_del_addr, .mtu = BUF_SIZE, .max_packet = BUF_SIZE, };