1 static char *rcsid = "$Id: relay.c,v 1.2 1996/08/01 02:21:08 paul Exp $";
2
3 /*
4 * relay
5 * =====
6 *
7 * The relay program serves as a packet switch and simulates an ethernet
8 * segment. When it starts up it opens a UDP/IP socket, and publishes its
9 * addresses in a file (whose name is specified as the single command
10 * line argument). It then waits for incoming ethernet packets. Each
11 * of the two "ethernet addresses" in the ethernet header is actually
12 * a UDP/IP address (both are 6 bytes!). Incoming packets fall into the
13 * following categories:
14 *
15 * 1. Destination address is our address. A packet from a new smx instance
16 * registering itself (its address is the source address).
17 * 2. A broadcast, that is forwarded to all registered parties.
18 * 3. A packet sent to some other destination. It is forwarded to that
19 * destination.
20 *
21 * It may seem inefficient to route all traffic through the relay (when
22 * traffic of type 3 can potentially be sent direct from one smx instance
23 * to another), but the fact that the SunOS library calls to enable direct
24 * sending are not available means that everything is done via relay.
25 *
26 * XXX Currently there is no way for an smx instance to "de-register",
27 * so if "relay" runs for a long time it may send up forwarding broadcasts
28 * to a lot of smx instances, many of which are long deceased.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <assert.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netdb.h>
41
42 #include "lib.h"
43
44 #define MIN_PKT_SIZE 12 /* packets must have at least dest and src addrs */
45
46 /*
47 * Local functions.
48 */
49 static void usage(void);
50
51 static int open_socket(u_short *port);
52 static int write_address_file(const char *file_name, u_short port);
53 static char *get_ourhostname(void);
54
55 static int relay_messages(int sock, u_short port);
56 static int is_our_machine(u_int host);
57 static unsigned int *our_ip_nums(void);
58 static void forward_mesg(int sock, u_int host, u_short port, char *buffer,
59 int buff_len);
60
61 static char *progname;
62
63
64 /*
65 * Function: main
66 * Parameters: argc, argv - the name of the address file must be the only
67 * command line argument.
68 *
69 * This function coordinates everthing by opening a socket, writing
70 * its address to a file, and the calling relay_messages to perform
71 * the relaying.
72 */
73 int main(int argc, char *argv[])
74 {
75 int sock;
76 u_short port;
77
78 progname = argv[0];
79
80 if (argc != 2) {
81 usage();
82 }
83
84 if ((sock = open_socket(&port)) == -1) {
85 fprintf(stderr, "%s: failed to open socket\n", progname);
86 exit(1);
87 }
88
89 if (write_address_file(argv[1], port) == -1) {
90 fprintf(stderr, "%s: failed to write address file\n", progname);
91 exit(1);
92 }
93
94 relay_messages(sock, port);
95
96 return 0;
97 }
98
99
100 /*
101 * Function: usage
102 *
103 * Prints a usage message then calls exit.
104 */
105 static void usage(void)
106 {
107 fprintf(stderr, "Usage: %s socket_address_file\n", progname);
108 exit(1);
109 }
110
111
112 /*
113 * Function: open_socket
114 * Parameter: port - used to return the port number of the UDP address
115 * assigned to the socket.
116 * Returns: the descriptor of the open socket on success, -1 on error
117 *
118 * Creates a UDP/IP socket, binds an address to it, and returns the port
119 * number for that address.
120 */
121 static int open_socket(u_short *port)
122 {
123 struct sockaddr_in dsock;
124 int addrlen;
125 int ds;
126
127 if ((ds = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
128 return -1;
129 }
130
131 dsock.sin_family = AF_INET;
132 dsock.sin_port = htons(0);
133 dsock.sin_addr.s_addr = htonl(INADDR_ANY);
134 if (bind(ds, (struct sockaddr *) &dsock, sizeof(dsock)) < 0) {
135 return -1;
136 }
137
138 addrlen = sizeof(dsock);
139 if (getsockname(ds, (struct sockaddr *) &dsock, &addrlen) == -1) {
140 return -1;
141 }
142
143 *port = ntohs(dsock.sin_port);
144 return ds;
145 }
146
147
148 /*
149 * Function: write_address_file
150 * Parameters: file_name - name of file to write to
151 * port - port number to write to file
152 * Returns: 0 on success, -1 on error
153 *
154 * Writes the host name of the current machine and the specified
155 * port number to the specified file.
156 */
157 static int write_address_file(const char *file_name, u_short port)
158 {
159 FILE *addr_file;
160
161 addr_file = fopen(file_name, "w");
162 if (addr_file == 0) {
163 return -1;
164 }
165 fprintf(addr_file, "%s %d\n", get_ourhostname(), port);
166 if (fflush(addr_file) == EOF || ferror(addr_file)) {
167 return -1;
168 }
169 if (fclose(addr_file) == EOF) {
170 return -1;
171 }
172
173 return 0;
174 }
175
176
177 /*
178 * Function: get_ourhostname
179 * Returns: a pointer to a static array containing the name of this
180 * (SunOS) machine.
181 */
182 static char *get_ourhostname(void)
183 {
184 static char our_name[MAXHOSTNAMELEN + 1];
185 static int first_time = 1;
186 int gethostname(char *name, int namelen); /* not in any .h file */
187
188 if (first_time) {
189 if (gethostname(our_name, sizeof(our_name)) == -1) {
190 fprintf(stderr, "%s: gethostname error %d\n", progname, errno);
191 exit(1);
192 }
193 first_time = 0;
194 }
195 return our_name;
196 }
197
198
199 /*
200 * Function: relay_messages
201 * Parameters: sock - socket on which to receive messages
202 * port - UDP port number bound to sock
203 * Returns: -1 on error, doesn't return otherwise
204 *
205 * Sits in an infinite loop receiving and processing messages. Any messages
206 * addressed to relay itself cause a new smx instance to be added to the
207 * list. Any broadcasts are forwarded to all registered smx instances.
208 * All other messages are forwarded to their specified destinations.
209 * The list of addresses of smx instances is held on the contacts array,
210 * which is dynamically increased in size (REALLOC_CHUNK elements at a time)
211 * as needed.
212 */
213 #define REALLOC_CHUNK 20
214
215 static int relay_messages(int sock, u_short port)
216 {
217 struct contact_list {
218 u_int ip;
219 u_short port;
220 } *contacts;
221 int array_size, num_contacts;
222
223 u_int to_host, from_host;
224 u_short to_port, from_port;
225 char buffer[2000];
226 int chars_read;
227
228 array_size = REALLOC_CHUNK;
229 num_contacts = 0;
230 if ((contacts = malloc(array_size * sizeof(*contacts))) == 0) {
231 return -1;
232 }
233
234 for (;;) {
235 chars_read = read(sock, buffer, sizeof(buffer));
236 #ifdef DEBUG
237 printf("Got message of %d chars\n", chars_read); fflush(stdout);
238 #endif
239 if (chars_read <= 0) {
240 fprintf(stderr, "%s: %d returned by read on socket; exiting\n",
241 progname, errno);
242 exit(1);
243 }
244 if (chars_read < MIN_PKT_SIZE) {
245 continue;
246 }
247 to_host = atohost(buffer + DEST_HOST);
248 to_port = atoport(buffer + DEST_PORT);
249 from_host = atohost(buffer + SRC_HOST);
250 from_port = atoport(buffer + SRC_PORT);
251 #ifdef DEBUG
252 printf("to: host 0x%x, port %d; ", to_host, to_port);
253 printf("from: host 0x%x, port = %d; our port = %d\n",
254 from_host, from_port, port);
255 fflush(stdout);
256 #endif
257
258 if (is_our_machine(to_host) && to_port == port) {
259 /*
260 * This message is directed to the relay server from an smx
261 * bootstrap---the UDP address of the new smx is in the source
262 * field.
263 */
264 if (num_contacts == array_size) {
265 array_size += REALLOC_CHUNK;
266 if ((contacts = realloc(contacts, array_size *
267 sizeof(*contacts))) == 0) {
268 return -1;
269 }
270 }
271 contacts[num_contacts].ip = from_host;
272 contacts[num_contacts].port = from_port;
273 #ifdef DEBUG
274 printf("relay: Adding host 0x%x/%d\n", from_host, from_port);
275 fflush(stdout);
276 #endif
277 num_contacts++;
278 continue;
279 }
280
281 if (to_host == 0xffffffff && to_port == 0xffff) {
282 int i;
283
284 /*
285 * Broacdast to everyone
286 */
287 for (i = 0; i < num_contacts; i++) {
288 if (contacts[i].ip == from_host &&
289 contacts[i].port == from_port) {
290 /* Hosts don't get their own broadcasts */
291 continue;
292 }
293 forward_mesg(sock, contacts[i].ip, contacts[i].port, buffer,
294 chars_read);
295 }
296 } else {
297 forward_mesg(sock, to_host, to_port, buffer, chars_read);
298 }
299 }
300 }
301
302
303 /*
304 * Function: is_our_machine
305 * Parameter: host - ip address to be checked
306 * Returns: true if host is an IP address of this machine, false otherwise.
307 */
308 static int is_our_machine(u_int host)
309 {
310 static u_int *our_nums = 0;
311 u_int *np;
312
313 if (our_nums == 0) {
314 if ((our_nums = our_ip_nums()) == 0) {
315 fprintf(stderr, "%s: falied to get local IP numbers\n", progname);
316 exit(1);
317 }
318 }
319
320 for (np = our_nums; *np; np++) {
321 if (*np == host) {
322 return 1;
323 }
324 }
325 return 0;
326 }
327
328
329 /*
330 * Function: our_ip_nums
331 * Returns: a pointer to a malloc'ed arrat containing all of the ip addresses
332 * for this machine returned by gethostname. The list is terminated
333 * by an ip address of 0.
334 */
335 static unsigned int *our_ip_nums(void)
336 {
337 struct hostent *hp;
338 u_int *nums;
339 u_int **ip;
340 int cnt;
341
342 if ((hp = gethostbyname(get_ourhostname())) == 0) {
343 return 0;
344 }
345
346 assert(sizeof(*nums) == hp->h_length);
347 ip = (unsigned int **) hp->h_addr_list;
348 for (cnt = 0; *ip; ip++, cnt++) {
349 ;
350 }
351 if ((nums = malloc((cnt + 1) * sizeof(*nums))) == 0) {
352 return 0;
353 }
354 ip = (unsigned int **) hp->h_addr_list;
355 for (cnt = 0; *ip; ip++, cnt++) {
356 nums[cnt] = **ip;
357 }
358 nums[cnt] = 0;
359 return nums;
360 }
361
362
363 /*
364 * Function: forward_mesg
365 * Parameters: sock - socket to send message on.
366 * host, port - UDP/IP address to send to
367 * buffer - packet to send
368 * buff_len - length of packet to send
369 *
370 * Sends the specified packet to the specified UDP/IP address on the specified
371 * socket.
372 */
373 static void forward_mesg(int sock, u_int host, u_short port, char *buffer,
374 int buff_len)
375 {
376 struct sockaddr_in addr;
377
378 addr.sin_family = AF_INET;
379 addr.sin_addr.s_addr = htonl(host);
380 addr.sin_port = htons(port);
381 if (sendto(sock, buffer, buff_len, 0, (struct sockaddr *) &addr,
382 sizeof(addr)) != buff_len) {
383 fprintf(stderr, "%s: send failed to %d/%d; errno %d\n", progname,
384 host, port, errno);
385 }
386 }
387
388
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.