b63a86046374a3d7440df77b8aff89b055e6f8d3
[linux-2.6.git] / arch / um / drivers / net_user.c
1 /* 
2  * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include <stddef.h>
7 #include <stdarg.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <errno.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <sys/wait.h>
15 #include <sys/time.h>
16 #include "user.h"
17 #include "kern_util.h"
18 #include "net_user.h"
19 #include "os.h"
20 #include "um_malloc.h"
21
22 int tap_open_common(void *dev, char *gate_addr)
23 {
24         int tap_addr[4];
25
26         if(gate_addr == NULL)
27                 return 0;
28         if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
29                   &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){
30                 printk("Invalid tap IP address - '%s'\n", gate_addr);
31                 return -EINVAL;
32         }
33         return 0;
34 }
35
36 void tap_check_ips(char *gate_addr, unsigned char *eth_addr)
37 {
38         int tap_addr[4];
39
40         if((gate_addr != NULL) && 
41            (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
42                    &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) &&
43            (eth_addr[0] == tap_addr[0]) && 
44            (eth_addr[1] == tap_addr[1]) && 
45            (eth_addr[2] == tap_addr[2]) && 
46            (eth_addr[3] == tap_addr[3])){
47                 printk("The tap IP address and the UML eth IP address"
48                        " must be different\n");
49         }
50 }
51
52 /* Do reliable error handling as this fails frequently enough. */
53 void read_output(int fd, char *output, int len)
54 {
55         int remain, ret, expected;
56         char c;
57         char *str;
58
59         if(output == NULL){
60                 output = &c;
61                 len = sizeof(c);
62         }
63                 
64         *output = '\0';
65         ret = os_read_file(fd, &remain, sizeof(remain));
66
67         if (ret != sizeof(remain)) {
68                 expected = sizeof(remain);
69                 str = "length";
70                 goto err;
71         }
72
73         while(remain != 0){
74                 expected = (remain < len) ? remain : len;
75                 ret = os_read_file(fd, output, expected);
76                 if (ret != expected) {
77                         str = "data";
78                         goto err;
79                 }
80                 remain -= ret;
81         }
82
83         return;
84
85 err:
86         if (ret < 0)
87                 printk("read_output - read of %s failed, errno = %d\n", str, -ret);
88         else
89                 printk("read_output - read of %s failed, read only %d of %d bytes\n", str, ret, expected);
90 }
91
92 int net_read(int fd, void *buf, int len)
93 {
94         int n;
95
96         n = os_read_file(fd,  buf,  len);
97
98         if(n == -EAGAIN)
99                 return 0;
100         else if(n == 0)
101                 return -ENOTCONN;
102         return n;
103 }
104
105 int net_recvfrom(int fd, void *buf, int len)
106 {
107         int n;
108
109         CATCH_EINTR(n = recvfrom(fd,  buf,  len, 0, NULL, NULL));
110         if(n < 0){
111                 if(errno == EAGAIN)
112                         return 0;
113                 return -errno;
114         }
115         else if(n == 0)
116                 return -ENOTCONN;
117         return n;
118 }
119
120 int net_write(int fd, void *buf, int len)
121 {
122         int n;
123
124         n = os_write_file(fd, buf, len);
125
126         if(n == -EAGAIN)
127                 return 0;
128         else if(n == 0)
129                 return -ENOTCONN;
130         return n;
131 }
132
133 int net_send(int fd, void *buf, int len)
134 {
135         int n;
136
137         CATCH_EINTR(n = send(fd, buf, len, 0));
138         if(n < 0){
139                 if(errno == EAGAIN)
140                         return 0;
141                 return -errno;
142         }
143         else if(n == 0)
144                 return -ENOTCONN;
145         return n;
146 }
147
148 int net_sendto(int fd, void *buf, int len, void *to, int sock_len)
149 {
150         int n;
151
152         CATCH_EINTR(n = sendto(fd, buf, len, 0, (struct sockaddr *) to,
153                                sock_len));
154         if(n < 0){
155                 if(errno == EAGAIN)
156                         return 0;
157                 return -errno;
158         }
159         else if(n == 0)
160                 return -ENOTCONN;
161         return n;
162 }
163
164 struct change_pre_exec_data {
165         int close_me;
166         int stdout;
167 };
168
169 static void change_pre_exec(void *arg)
170 {
171         struct change_pre_exec_data *data = arg;
172
173         os_close_file(data->close_me);
174         dup2(data->stdout, 1);
175 }
176
177 static int change_tramp(char **argv, char *output, int output_len)
178 {
179         int pid, fds[2], err;
180         struct change_pre_exec_data pe_data;
181
182         err = os_pipe(fds, 1, 0);
183         if(err < 0){
184                 printk("change_tramp - pipe failed, err = %d\n", -err);
185                 return err;
186         }
187         pe_data.close_me = fds[0];
188         pe_data.stdout = fds[1];
189         pid = run_helper(change_pre_exec, &pe_data, argv, NULL);
190
191         if (pid > 0)    /* Avoid hang as we won't get data in failure case. */
192                 read_output(fds[0], output, output_len);
193
194         os_close_file(fds[0]);
195         os_close_file(fds[1]);
196
197         if (pid > 0)
198                 CATCH_EINTR(err = waitpid(pid, NULL, 0));
199         return pid;
200 }
201
202 static void change(char *dev, char *what, unsigned char *addr,
203                    unsigned char *netmask)
204 {
205         char addr_buf[sizeof("255.255.255.255\0")];
206         char netmask_buf[sizeof("255.255.255.255\0")];
207         char version[sizeof("nnnnn\0")];
208         char *argv[] = { "uml_net", version, what, dev, addr_buf, 
209                          netmask_buf, NULL };
210         char *output;
211         int output_len, pid;
212
213         sprintf(version, "%d", UML_NET_VERSION);
214         sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
215         sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], 
216                 netmask[2], netmask[3]);
217
218         output_len = page_size();
219         output = um_kmalloc(output_len);
220         if(output == NULL)
221                 printk("change : failed to allocate output buffer\n");
222
223         pid = change_tramp(argv, output, output_len);
224         if(pid < 0) return;
225
226         if(output != NULL){
227                 printk("%s", output);
228                 kfree(output);
229         }
230 }
231
232 void open_addr(unsigned char *addr, unsigned char *netmask, void *arg)
233 {
234         change(arg, "add", addr, netmask);
235 }
236
237 void close_addr(unsigned char *addr, unsigned char *netmask, void *arg)
238 {
239         change(arg, "del", addr, netmask);
240 }
241
242 char *split_if_spec(char *str, ...)
243 {
244         char **arg, *end;
245         va_list ap;
246
247         va_start(ap, str);
248         while((arg = va_arg(ap, char **)) != NULL){
249                 if(*str == '\0')
250                         return NULL;
251                 end = strchr(str, ',');
252                 if(end != str)
253                         *arg = str;
254                 if(end == NULL)
255                         return NULL;
256                 *end++ = '\0';
257                 str = end;
258         }
259         va_end(ap);
260         return str;
261 }