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