-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcs431vde.c
More file actions
136 lines (117 loc) · 3.7 KB
/
Copy pathcs431vde.c
File metadata and controls
136 lines (117 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* cs431vde.c
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
/* These functions manage the fact that VDE expects the first 2 octets written
* to be the length of the frame, in octets, in big-endian format. Therefore,
* send_ethernet_frame adds those and receive_ethernet_frame removes them. */
ssize_t
receive_ethernet_frame(int fd, void *buf)
{
uint16_t nbo_len, len;
read(fd, &nbo_len, 2);
len = ntohs(nbo_len);
read(fd, buf, len);
return len;
}
void
send_ethernet_frame(int fd, void *frame, uint16_t len)
{
uint16_t nbo_len;
nbo_len = htons(len);
write(fd, &nbo_len, 2);
write(fd, frame, len);
}
/* This function takes the place of dpipe(1), shipped with vde, which is
* unpleasantly restrictive: it requires the process use stdout and stdin to
* send and receive packets, respectively, and thus doesn't permit a process
* to connect to multiple switches (necessary for implementing a router) and
* prevents the use of printf.
*
* Ultimately, we need to fork and exec a process in which to run vde_plug,
* which is what actually communicates with the vde_switch instance. Since we
* want bidirectional communication (send frames to the switch, receive frames
* from the switch), we need two pipes: to_plug_fds will be used to send
* frames from this process to the switch (via the vde_plug process);
* from_plug_fds will be used by this process to receive frames from the
* switch (again, via the vde_plug process).
*
* - to_plug_fds[0]: used by the vde_plug process to receive frames for the
* switch
* - to_plug_fds[1]: used by this process to send frames to the vde_plug
* process, and thence to the switch
* - from_plug_fds[0]: used by this process to receive frames from the
* vde_plug process, which it received from the switch
* - from_plug_fds[1]: used by the vde_plug process to send frames to this
* process
*
* A return value of 0 indicates success. A non-zero return value indicates
* failure, and errno will be set accordingly.
*
* Upon successful completion, frames can be read from fds[0] and written to
* fds[1].
*
* The second parameter is the command to execute. When using a local switch,
* this will be "vde_plug"; when using a remote switch, this will be an ssh
* command that in turn calls vde_plug.
*/
int
connect_to_vde_switch(int fds[2], char *cmd[])
{
int to_plug_fds[2];
int from_plug_fds[2];
int err;
pid_t child_pid;
/* create pipes */
if(pipe(to_plug_fds) < 0) {
err = errno;
perror("pipe");
errno = err;
return -1;
}
if(pipe(from_plug_fds) < 0) {
err = errno;
close(to_plug_fds[0]);
close(to_plug_fds[1]);
perror("pipe");
errno = err;
return -1;
}
/* fork & exec vde_plug */
/* Note that we brazenly assume all the close(2) and dup2(2) calls
* succeed. Naughty, naughty. */
child_pid = fork();
if(child_pid == -1) {
err = errno;
close(to_plug_fds[0]);
close(to_plug_fds[1]);
close(from_plug_fds[0]);
close(from_plug_fds[1]);
errno = err;
return -1;
}
if(child_pid == 0) {
close(to_plug_fds[1]);
close(from_plug_fds[0]);
dup2(to_plug_fds[0], 0);
dup2(from_plug_fds[1], 1);
close(to_plug_fds[0]);
close(from_plug_fds[1]);
if(execvp(cmd[0], cmd) < 0) {
perror("execlp");
exit(1);
}
}
/* record the pipe file descriptors that matter to this process */
fds[0] = from_plug_fds[0];
fds[1] = to_plug_fds[1];
/* clean up the others */
close(from_plug_fds[1]);
close(to_plug_fds[0]);
return 0;
}