]>
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; | |
4dbed85a | 97 | int pid, err, n; |
1da177e4 LT |
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"); | |
4dbed85a | 134 | err = helper_wait(pid, 0, "uml_net"); |
1da177e4 | 135 | } |
56bd194b | 136 | return err; |
1da177e4 LT |
137 | } |
138 | ||
139 | static int etap_open(void *data) | |
140 | { | |
141 | struct ethertap_data *pri = data; | |
142 | char *output; | |
143 | int data_fds[2], control_fds[2], err, output_len; | |
144 | ||
145 | err = tap_open_common(pri->dev, pri->gate_addr); | |
cd1ae0e4 | 146 | if (err) |
56bd194b | 147 | return err; |
1da177e4 | 148 | |
512b6fb1 | 149 | err = socketpair(AF_UNIX, SOCK_DGRAM, 0, data_fds); |
cd1ae0e4 | 150 | if (err) { |
512b6fb1 | 151 | err = -errno; |
cd1ae0e4 JD |
152 | printk(UM_KERN_ERR "etap_open - data socketpair failed - " |
153 | "err = %d\n", errno); | |
56bd194b | 154 | return err; |
1da177e4 LT |
155 | } |
156 | ||
512b6fb1 | 157 | err = socketpair(AF_UNIX, SOCK_STREAM, 0, control_fds); |
cd1ae0e4 | 158 | if (err) { |
512b6fb1 | 159 | err = -errno; |
cd1ae0e4 JD |
160 | printk(UM_KERN_ERR "etap_open - control socketpair failed - " |
161 | "err = %d\n", errno); | |
512b6fb1 | 162 | goto out_close_data; |
1da177e4 | 163 | } |
56bd194b | 164 | |
512b6fb1 | 165 | err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0], |
1da177e4 | 166 | control_fds[1], data_fds[0], data_fds[1]); |
1ffb9164 | 167 | output_len = UM_KERN_PAGE_SIZE; |
e4c4bf99 | 168 | output = kmalloc(output_len, UM_GFP_KERNEL); |
1da177e4 LT |
169 | read_output(control_fds[0], output, output_len); |
170 | ||
cd1ae0e4 JD |
171 | if (output == NULL) |
172 | printk(UM_KERN_ERR "etap_open : failed to allocate output " | |
173 | "buffer\n"); | |
1da177e4 LT |
174 | else { |
175 | printk("%s", output); | |
176 | kfree(output); | |
177 | } | |
178 | ||
cd1ae0e4 JD |
179 | if (err < 0) { |
180 | printk(UM_KERN_ERR "etap_tramp failed - err = %d\n", -err); | |
512b6fb1 | 181 | goto out_close_control; |
1da177e4 LT |
182 | } |
183 | ||
184 | pri->data_fd = data_fds[0]; | |
185 | pri->control_fd = control_fds[0]; | |
186 | iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); | |
56bd194b | 187 | return data_fds[0]; |
512b6fb1 JD |
188 | |
189 | out_close_control: | |
190 | close(control_fds[0]); | |
191 | close(control_fds[1]); | |
192 | out_close_data: | |
193 | close(data_fds[0]); | |
194 | close(data_fds[1]); | |
195 | return err; | |
1da177e4 LT |
196 | } |
197 | ||
198 | static void etap_close(int fd, void *data) | |
199 | { | |
200 | struct ethertap_data *pri = data; | |
201 | ||
202 | iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); | |
512b6fb1 JD |
203 | close(fd); |
204 | ||
cd1ae0e4 JD |
205 | if (shutdown(pri->data_fd, SHUT_RDWR) < 0) |
206 | printk(UM_KERN_ERR "etap_close - shutdown data socket failed, " | |
512b6fb1 JD |
207 | "errno = %d\n", errno); |
208 | ||
cd1ae0e4 JD |
209 | if (shutdown(pri->control_fd, SHUT_RDWR) < 0) |
210 | printk(UM_KERN_ERR "etap_close - shutdown control socket " | |
211 | "failed, errno = %d\n", errno); | |
212 | ||
512b6fb1 | 213 | close(pri->data_fd); |
1da177e4 | 214 | pri->data_fd = -1; |
512b6fb1 | 215 | close(pri->control_fd); |
1da177e4 LT |
216 | pri->control_fd = -1; |
217 | } | |
218 | ||
1da177e4 LT |
219 | static void etap_add_addr(unsigned char *addr, unsigned char *netmask, |
220 | void *data) | |
221 | { | |
222 | struct ethertap_data *pri = data; | |
223 | ||
224 | tap_check_ips(pri->gate_addr, addr); | |
cd1ae0e4 | 225 | if (pri->control_fd == -1) |
56bd194b | 226 | return; |
1da177e4 LT |
227 | etap_open_addr(addr, netmask, &pri->control_fd); |
228 | } | |
229 | ||
512b6fb1 | 230 | static void etap_del_addr(unsigned char *addr, unsigned char *netmask, |
1da177e4 LT |
231 | void *data) |
232 | { | |
233 | struct ethertap_data *pri = data; | |
234 | ||
cd1ae0e4 | 235 | if (pri->control_fd == -1) |
56bd194b | 236 | return; |
512b6fb1 | 237 | |
1da177e4 LT |
238 | etap_close_addr(addr, netmask, &pri->control_fd); |
239 | } | |
240 | ||
5e7672ec | 241 | const struct net_user_info ethertap_user_info = { |
1da177e4 LT |
242 | .init = etap_user_init, |
243 | .open = etap_open, | |
244 | .close = etap_close, | |
245 | .remove = NULL, | |
1da177e4 LT |
246 | .add_address = etap_add_addr, |
247 | .delete_address = etap_del_addr, | |
b53f35a8 JD |
248 | .mtu = ETH_MAX_PACKET, |
249 | .max_packet = ETH_MAX_PACKET + ETH_HEADER_ETHERTAP, | |
1da177e4 | 250 | }; |