]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | |
3 | * James Leu (jleu@mindspring.net). | |
4 | * Copyright (C) 2001 by various other people who didn't put their name here. | |
5 | * Licensed under the GPL. | |
6 | */ | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <unistd.h> | |
10 | #include <stddef.h> | |
11 | #include <stdlib.h> | |
12 | #include <sys/errno.h> | |
13 | #include <sys/socket.h> | |
14 | #include <sys/wait.h> | |
15 | #include <sys/un.h> | |
16 | #include <net/if.h> | |
17 | #include "user.h" | |
18 | #include "kern_util.h" | |
19 | #include "user_util.h" | |
20 | #include "net_user.h" | |
21 | #include "etap.h" | |
22 | #include "helper.h" | |
23 | #include "os.h" | |
24 | ||
25 | #define MAX_PACKET ETH_MAX_PACKET | |
26 | ||
27 | void etap_user_init(void *data, void *dev) | |
28 | { | |
29 | struct ethertap_data *pri = data; | |
30 | ||
31 | pri->dev = dev; | |
32 | } | |
33 | ||
34 | struct addr_change { | |
35 | enum { ADD_ADDR, DEL_ADDR } what; | |
36 | unsigned char addr[4]; | |
37 | unsigned char netmask[4]; | |
38 | }; | |
39 | ||
40 | static void etap_change(int op, unsigned char *addr, unsigned char *netmask, | |
41 | int fd) | |
42 | { | |
43 | struct addr_change change; | |
44 | void *output; | |
45 | int n; | |
46 | ||
47 | change.what = op; | |
48 | memcpy(change.addr, addr, sizeof(change.addr)); | |
49 | memcpy(change.netmask, netmask, sizeof(change.netmask)); | |
50 | n = os_write_file(fd, &change, sizeof(change)); | |
51 | if(n != sizeof(change)) | |
52 | printk("etap_change - request failed, err = %d\n", -n); | |
53 | output = um_kmalloc(page_size()); | |
54 | if(output == NULL) | |
55 | printk("etap_change : Failed to allocate output buffer\n"); | |
56 | read_output(fd, output, page_size()); | |
57 | if(output != NULL){ | |
58 | printk("%s", output); | |
59 | kfree(output); | |
60 | } | |
61 | } | |
62 | ||
63 | static void etap_open_addr(unsigned char *addr, unsigned char *netmask, | |
64 | void *arg) | |
65 | { | |
66 | etap_change(ADD_ADDR, addr, netmask, *((int *) arg)); | |
67 | } | |
68 | ||
69 | static void etap_close_addr(unsigned char *addr, unsigned char *netmask, | |
70 | void *arg) | |
71 | { | |
72 | etap_change(DEL_ADDR, addr, netmask, *((int *) arg)); | |
73 | } | |
74 | ||
75 | struct etap_pre_exec_data { | |
76 | int control_remote; | |
77 | int control_me; | |
78 | int data_me; | |
79 | }; | |
80 | ||
81 | static void etap_pre_exec(void *arg) | |
82 | { | |
83 | struct etap_pre_exec_data *data = arg; | |
84 | ||
85 | dup2(data->control_remote, 1); | |
86 | os_close_file(data->data_me); | |
87 | os_close_file(data->control_me); | |
88 | } | |
89 | ||
90 | static int etap_tramp(char *dev, char *gate, int control_me, | |
91 | int control_remote, int data_me, int data_remote) | |
92 | { | |
93 | struct etap_pre_exec_data pe_data; | |
94 | int pid, status, err, n; | |
95 | char version_buf[sizeof("nnnnn\0")]; | |
96 | char data_fd_buf[sizeof("nnnnnn\0")]; | |
97 | char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; | |
98 | char *setup_args[] = { "uml_net", version_buf, "ethertap", dev, | |
99 | data_fd_buf, gate_buf, NULL }; | |
100 | char *nosetup_args[] = { "uml_net", version_buf, "ethertap", | |
101 | dev, data_fd_buf, NULL }; | |
102 | char **args, c; | |
103 | ||
104 | sprintf(data_fd_buf, "%d", data_remote); | |
105 | sprintf(version_buf, "%d", UML_NET_VERSION); | |
106 | if(gate != NULL){ | |
107 | strcpy(gate_buf, gate); | |
108 | args = setup_args; | |
109 | } | |
110 | else args = nosetup_args; | |
111 | ||
112 | err = 0; | |
113 | pe_data.control_remote = control_remote; | |
114 | pe_data.control_me = control_me; | |
115 | pe_data.data_me = data_me; | |
116 | pid = run_helper(etap_pre_exec, &pe_data, args, NULL); | |
117 | ||
118 | if(pid < 0) err = pid; | |
119 | os_close_file(data_remote); | |
120 | os_close_file(control_remote); | |
121 | n = os_read_file(control_me, &c, sizeof(c)); | |
122 | if(n != sizeof(c)){ | |
123 | printk("etap_tramp : read of status failed, err = %d\n", -n); | |
124 | return(-EINVAL); | |
125 | } | |
126 | if(c != 1){ | |
127 | printk("etap_tramp : uml_net failed\n"); | |
128 | err = -EINVAL; | |
129 | CATCH_EINTR(n = waitpid(pid, &status, 0)); | |
130 | if(n < 0) | |
131 | err = -errno; | |
132 | else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)) | |
133 | printk("uml_net didn't exit with status 1\n"); | |
134 | } | |
135 | return(err); | |
136 | } | |
137 | ||
138 | static int etap_open(void *data) | |
139 | { | |
140 | struct ethertap_data *pri = data; | |
141 | char *output; | |
142 | int data_fds[2], control_fds[2], err, output_len; | |
143 | ||
144 | err = tap_open_common(pri->dev, pri->gate_addr); | |
145 | if(err) return(err); | |
146 | ||
147 | err = os_pipe(data_fds, 0, 0); | |
148 | if(err < 0){ | |
149 | printk("data os_pipe failed - err = %d\n", -err); | |
150 | return(err); | |
151 | } | |
152 | ||
153 | err = os_pipe(control_fds, 1, 0); | |
154 | if(err < 0){ | |
155 | printk("control os_pipe failed - err = %d\n", -err); | |
156 | return(err); | |
157 | } | |
158 | ||
159 | err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0], | |
160 | control_fds[1], data_fds[0], data_fds[1]); | |
161 | output_len = page_size(); | |
162 | output = um_kmalloc(output_len); | |
163 | read_output(control_fds[0], output, output_len); | |
164 | ||
165 | if(output == NULL) | |
166 | printk("etap_open : failed to allocate output buffer\n"); | |
167 | else { | |
168 | printk("%s", output); | |
169 | kfree(output); | |
170 | } | |
171 | ||
172 | if(err < 0){ | |
173 | printk("etap_tramp failed - err = %d\n", -err); | |
174 | return(err); | |
175 | } | |
176 | ||
177 | pri->data_fd = data_fds[0]; | |
178 | pri->control_fd = control_fds[0]; | |
179 | iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); | |
180 | return(data_fds[0]); | |
181 | } | |
182 | ||
183 | static void etap_close(int fd, void *data) | |
184 | { | |
185 | struct ethertap_data *pri = data; | |
186 | ||
187 | iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); | |
188 | os_close_file(fd); | |
189 | os_shutdown_socket(pri->data_fd, 1, 1); | |
190 | os_close_file(pri->data_fd); | |
191 | pri->data_fd = -1; | |
192 | os_close_file(pri->control_fd); | |
193 | pri->control_fd = -1; | |
194 | } | |
195 | ||
196 | static int etap_set_mtu(int mtu, void *data) | |
197 | { | |
198 | return(mtu); | |
199 | } | |
200 | ||
201 | static void etap_add_addr(unsigned char *addr, unsigned char *netmask, | |
202 | void *data) | |
203 | { | |
204 | struct ethertap_data *pri = data; | |
205 | ||
206 | tap_check_ips(pri->gate_addr, addr); | |
207 | if(pri->control_fd == -1) return; | |
208 | etap_open_addr(addr, netmask, &pri->control_fd); | |
209 | } | |
210 | ||
211 | static void etap_del_addr(unsigned char *addr, unsigned char *netmask, | |
212 | void *data) | |
213 | { | |
214 | struct ethertap_data *pri = data; | |
215 | ||
216 | if(pri->control_fd == -1) return; | |
217 | etap_close_addr(addr, netmask, &pri->control_fd); | |
218 | } | |
219 | ||
220 | struct net_user_info ethertap_user_info = { | |
221 | .init = etap_user_init, | |
222 | .open = etap_open, | |
223 | .close = etap_close, | |
224 | .remove = NULL, | |
225 | .set_mtu = etap_set_mtu, | |
226 | .add_address = etap_add_addr, | |
227 | .delete_address = etap_del_addr, | |
228 | .max_packet = MAX_PACKET - ETH_HEADER_ETHERTAP | |
229 | }; | |
230 | ||
231 | /* | |
232 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
233 | * Emacs will notice this stuff at the end of the file and automatically | |
234 | * adjust the settings for this buffer only. This must remain at the end | |
235 | * of the file. | |
236 | * --------------------------------------------------------------------------- | |
237 | * Local variables: | |
238 | * c-file-style: "linux" | |
239 | * End: | |
240 | */ |