Commit | Line | Data |
---|---|---|
97870c34 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
cd1ae0e4 | 3 | * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
ef0470c0 | 4 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and |
1da177e4 LT |
5 | * James Leu (jleu@mindspring.net). |
6 | * Copyright (C) 2001 by various other people who didn't put their name here. | |
1da177e4 LT |
7 | */ |
8 | ||
9 | #include <stdio.h> | |
10 | #include <unistd.h> | |
cd1ae0e4 JD |
11 | #include <errno.h> |
12 | #include <string.h> | |
1da177e4 LT |
13 | #include <sys/socket.h> |
14 | #include <sys/wait.h> | |
1da177e4 | 15 | #include "etap.h" |
37185b33 AV |
16 | #include <os.h> |
17 | #include <net_user.h> | |
18 | #include <um_malloc.h> | |
1da177e4 LT |
19 | |
20 | #define MAX_PACKET ETH_MAX_PACKET | |
21 | ||
f34d9d2d | 22 | static int etap_user_init(void *data, void *dev) |
1da177e4 LT |
23 | { |
24 | struct ethertap_data *pri = data; | |
25 | ||
26 | pri->dev = dev; | |
f34d9d2d | 27 | return 0; |
1da177e4 LT |
28 | } |
29 | ||
30 | struct addr_change { | |
31 | enum { ADD_ADDR, DEL_ADDR } what; | |
32 | unsigned char addr[4]; | |
33 | unsigned char netmask[4]; | |
34 | }; | |
35 | ||
36 | static void etap_change(int op, unsigned char *addr, unsigned char *netmask, | |
37 | int fd) | |
38 | { | |
39 | struct addr_change change; | |
802e3077 | 40 | char *output; |
1da177e4 LT |
41 | int n; |
42 | ||
43 | change.what = op; | |
44 | memcpy(change.addr, addr, sizeof(change.addr)); | |
45 | memcpy(change.netmask, netmask, sizeof(change.netmask)); | |
a61f334f | 46 | CATCH_EINTR(n = write(fd, &change, sizeof(change))); |
cd1ae0e4 JD |
47 | if (n != sizeof(change)) { |
48 | printk(UM_KERN_ERR "etap_change - request failed, err = %d\n", | |
49 | errno); | |
ef0470c0 JD |
50 | return; |
51 | } | |
52 | ||
43f5b308 | 53 | output = uml_kmalloc(UM_KERN_PAGE_SIZE, UM_GFP_KERNEL); |
cd1ae0e4 JD |
54 | if (output == NULL) |
55 | printk(UM_KERN_ERR "etap_change : Failed to allocate output " | |
56 | "buffer\n"); | |
1ffb9164 | 57 | read_output(fd, output, UM_KERN_PAGE_SIZE); |
cd1ae0e4 | 58 | if (output != NULL) { |
1da177e4 LT |
59 | printk("%s", output); |
60 | kfree(output); | |
61 | } | |
62 | } | |
63 | ||
64 | static void etap_open_addr(unsigned char *addr, unsigned char *netmask, | |
65 | void *arg) | |
66 | { | |
67 | etap_change(ADD_ADDR, addr, netmask, *((int *) arg)); | |
68 | } | |
69 | ||
70 | static void etap_close_addr(unsigned char *addr, unsigned char *netmask, | |
71 | void *arg) | |
72 | { | |
73 | etap_change(DEL_ADDR, addr, netmask, *((int *) arg)); | |
74 | } | |
75 | ||
76 | struct etap_pre_exec_data { | |
77 | int control_remote; | |
78 | int control_me; | |
79 | int data_me; | |
80 | }; | |
81 | ||
82 | static void etap_pre_exec(void *arg) | |
83 | { | |
84 | struct etap_pre_exec_data *data = arg; | |
85 | ||
86 | dup2(data->control_remote, 1); | |
512b6fb1 JD |
87 | close(data->data_me); |
88 | close(data->control_me); | |
1da177e4 LT |
89 | } |
90 | ||
512b6fb1 | 91 | static int etap_tramp(char *dev, char *gate, int control_me, |
1da177e4 LT |
92 | int control_remote, int data_me, int data_remote) |
93 | { | |
94 | struct etap_pre_exec_data pe_data; | |
4dbed85a | 95 | int pid, err, n; |
1da177e4 LT |
96 | char version_buf[sizeof("nnnnn\0")]; |
97 | char data_fd_buf[sizeof("nnnnnn\0")]; | |
98 | char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; | |
99 | char *setup_args[] = { "uml_net", version_buf, "ethertap", dev, | |
100 | data_fd_buf, gate_buf, NULL }; | |
512b6fb1 | 101 | char *nosetup_args[] = { "uml_net", version_buf, "ethertap", |
1da177e4 LT |
102 | dev, data_fd_buf, NULL }; |
103 | char **args, c; | |
104 | ||
105 | sprintf(data_fd_buf, "%d", data_remote); | |
106 | sprintf(version_buf, "%d", UML_NET_VERSION); | |
cd1ae0e4 | 107 | if (gate != NULL) { |
7879b1d9 | 108 | strncpy(gate_buf, gate, 15); |
1da177e4 LT |
109 | args = setup_args; |
110 | } | |
111 | else args = nosetup_args; | |
112 | ||
113 | err = 0; | |
114 | pe_data.control_remote = control_remote; | |
115 | pe_data.control_me = control_me; | |
116 | pe_data.data_me = data_me; | |
c4399016 | 117 | pid = run_helper(etap_pre_exec, &pe_data, args); |
1da177e4 | 118 | |
cd1ae0e4 | 119 | if (pid < 0) |
ef0470c0 | 120 | err = pid; |
512b6fb1 JD |
121 | close(data_remote); |
122 | close(control_remote); | |
a61f334f | 123 | CATCH_EINTR(n = read(control_me, &c, sizeof(c))); |
cd1ae0e4 | 124 | if (n != sizeof(c)) { |
a61f334f | 125 | err = -errno; |
cd1ae0e4 JD |
126 | printk(UM_KERN_ERR "etap_tramp : read of status failed, " |
127 | "err = %d\n", -err); | |
a61f334f | 128 | return err; |
1da177e4 | 129 | } |
cd1ae0e4 JD |
130 | if (c != 1) { |
131 | printk(UM_KERN_ERR "etap_tramp : uml_net failed\n"); | |
1aa351a3 | 132 | err = helper_wait(pid); |
1da177e4 | 133 | } |
56bd194b | 134 | return err; |
1da177e4 LT |
135 | } |
136 | ||
137 | static int etap_open(void *data) | |
138 | { | |
139 | struct ethertap_data *pri = data; | |
140 | char *output; | |
141 | int data_fds[2], control_fds[2], err, output_len; | |
142 | ||
143 | err = tap_open_common(pri->dev, pri->gate_addr); | |
cd1ae0e4 | 144 | if (err) |
56bd194b | 145 | return err; |
1da177e4 | 146 | |
512b6fb1 | 147 | err = socketpair(AF_UNIX, SOCK_DGRAM, 0, data_fds); |
cd1ae0e4 | 148 | if (err) { |
512b6fb1 | 149 | err = -errno; |
cd1ae0e4 JD |
150 | printk(UM_KERN_ERR "etap_open - data socketpair failed - " |
151 | "err = %d\n", errno); | |
56bd194b | 152 | return err; |
1da177e4 LT |
153 | } |
154 | ||
512b6fb1 | 155 | err = socketpair(AF_UNIX, SOCK_STREAM, 0, control_fds); |
cd1ae0e4 | 156 | if (err) { |
512b6fb1 | 157 | err = -errno; |
cd1ae0e4 JD |
158 | printk(UM_KERN_ERR "etap_open - control socketpair failed - " |
159 | "err = %d\n", errno); | |
512b6fb1 | 160 | goto out_close_data; |
1da177e4 | 161 | } |
56bd194b | 162 | |
512b6fb1 | 163 | err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0], |
1da177e4 | 164 | control_fds[1], data_fds[0], data_fds[1]); |
1ffb9164 | 165 | output_len = UM_KERN_PAGE_SIZE; |
43f5b308 | 166 | output = uml_kmalloc(output_len, UM_GFP_KERNEL); |
1da177e4 LT |
167 | read_output(control_fds[0], output, output_len); |
168 | ||
cd1ae0e4 JD |
169 | if (output == NULL) |
170 | printk(UM_KERN_ERR "etap_open : failed to allocate output " | |
171 | "buffer\n"); | |
1da177e4 LT |
172 | else { |
173 | printk("%s", output); | |
174 | kfree(output); | |
175 | } | |
176 | ||
cd1ae0e4 JD |
177 | if (err < 0) { |
178 | printk(UM_KERN_ERR "etap_tramp failed - err = %d\n", -err); | |
512b6fb1 | 179 | goto out_close_control; |
1da177e4 LT |
180 | } |
181 | ||
182 | pri->data_fd = data_fds[0]; | |
183 | pri->control_fd = control_fds[0]; | |
184 | iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); | |
56bd194b | 185 | return data_fds[0]; |
512b6fb1 JD |
186 | |
187 | out_close_control: | |
188 | close(control_fds[0]); | |
189 | close(control_fds[1]); | |
190 | out_close_data: | |
191 | close(data_fds[0]); | |
192 | close(data_fds[1]); | |
193 | return err; | |
1da177e4 LT |
194 | } |
195 | ||
196 | static void etap_close(int fd, void *data) | |
197 | { | |
198 | struct ethertap_data *pri = data; | |
199 | ||
200 | iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); | |
512b6fb1 JD |
201 | close(fd); |
202 | ||
cd1ae0e4 JD |
203 | if (shutdown(pri->data_fd, SHUT_RDWR) < 0) |
204 | printk(UM_KERN_ERR "etap_close - shutdown data socket failed, " | |
512b6fb1 JD |
205 | "errno = %d\n", errno); |
206 | ||
cd1ae0e4 JD |
207 | if (shutdown(pri->control_fd, SHUT_RDWR) < 0) |
208 | printk(UM_KERN_ERR "etap_close - shutdown control socket " | |
209 | "failed, errno = %d\n", errno); | |
210 | ||
512b6fb1 | 211 | close(pri->data_fd); |
1da177e4 | 212 | pri->data_fd = -1; |
512b6fb1 | 213 | close(pri->control_fd); |
1da177e4 LT |
214 | pri->control_fd = -1; |
215 | } | |
216 | ||
1da177e4 LT |
217 | static void etap_add_addr(unsigned char *addr, unsigned char *netmask, |
218 | void *data) | |
219 | { | |
220 | struct ethertap_data *pri = data; | |
221 | ||
222 | tap_check_ips(pri->gate_addr, addr); | |
cd1ae0e4 | 223 | if (pri->control_fd == -1) |
56bd194b | 224 | return; |
1da177e4 LT |
225 | etap_open_addr(addr, netmask, &pri->control_fd); |
226 | } | |
227 | ||
512b6fb1 | 228 | static void etap_del_addr(unsigned char *addr, unsigned char *netmask, |
1da177e4 LT |
229 | void *data) |
230 | { | |
231 | struct ethertap_data *pri = data; | |
232 | ||
cd1ae0e4 | 233 | if (pri->control_fd == -1) |
56bd194b | 234 | return; |
512b6fb1 | 235 | |
1da177e4 LT |
236 | etap_close_addr(addr, netmask, &pri->control_fd); |
237 | } | |
238 | ||
5e7672ec | 239 | const struct net_user_info ethertap_user_info = { |
1da177e4 LT |
240 | .init = etap_user_init, |
241 | .open = etap_open, | |
242 | .close = etap_close, | |
243 | .remove = NULL, | |
1da177e4 LT |
244 | .add_address = etap_add_addr, |
245 | .delete_address = etap_del_addr, | |
b53f35a8 JD |
246 | .mtu = ETH_MAX_PACKET, |
247 | .max_packet = ETH_MAX_PACKET + ETH_HEADER_ETHERTAP, | |
1da177e4 | 248 | }; |