Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/udhcp/dhcprelay.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 7854 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

1 niro 532 /* vi: set sw=4 ts=4: */
2     /* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
3     *
4     * Licensed under GPL v2, see file LICENSE in this tarball for details.
5     *
6     * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
7     * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
8     * Zuercher Hochschule Winterthur,
9     * Netbeat AG
10     * Upstream has GPL v2 or later
11     */
12    
13     #include "common.h"
14     #include "dhcpd.h"
15     #include "options.h"
16    
17     /* constants */
18     #define SELECT_TIMEOUT 5 /* select timeout in sec. */
19     #define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
20     #define MAX_INTERFACES 9
21    
22    
23     /* This list holds information about clients. The xid_* functions manipulate this list. */
24     static struct xid_item {
25     u_int32_t xid;
26     struct sockaddr_in ip;
27     int client;
28     time_t timestamp;
29     struct xid_item *next;
30     } dhcprelay_xid_list = {0, {0}, 0, 0, NULL};
31    
32    
33     static struct xid_item * xid_add(u_int32_t xid, struct sockaddr_in *ip, int client)
34     {
35     struct xid_item *item;
36    
37     /* create new xid entry */
38     item = xmalloc(sizeof(struct xid_item));
39    
40     /* add xid entry */
41     item->ip = *ip;
42     item->xid = xid;
43     item->client = client;
44     item->timestamp = time(NULL);
45     item->next = dhcprelay_xid_list.next;
46     dhcprelay_xid_list.next = item;
47    
48     return item;
49     }
50    
51    
52     static void xid_expire(void)
53     {
54     struct xid_item *item = dhcprelay_xid_list.next;
55     struct xid_item *last = &dhcprelay_xid_list;
56     time_t current_time = time(NULL);
57    
58     while (item != NULL) {
59     if ((current_time-item->timestamp) > MAX_LIFETIME) {
60     last->next = item->next;
61     free(item);
62     item = last->next;
63     } else {
64     last = item;
65     item = item->next;
66     }
67     }
68     }
69    
70     static struct xid_item * xid_find(u_int32_t xid)
71     {
72     struct xid_item *item = dhcprelay_xid_list.next;
73     while (item != NULL) {
74     if (item->xid == xid) {
75     return item;
76     }
77     item = item->next;
78     }
79     return NULL;
80     }
81    
82     static void xid_del(u_int32_t xid)
83     {
84     struct xid_item *item = dhcprelay_xid_list.next;
85     struct xid_item *last = &dhcprelay_xid_list;
86     while (item != NULL) {
87     if (item->xid == xid) {
88     last->next = item->next;
89     free(item);
90     item = last->next;
91     } else {
92     last = item;
93     item = item->next;
94     }
95     }
96     }
97    
98    
99     /**
100     * get_dhcp_packet_type - gets the message type of a dhcp packet
101     * p - pointer to the dhcp packet
102     * returns the message type on success, -1 otherwise
103     */
104     static int get_dhcp_packet_type(struct dhcpMessage *p)
105     {
106     uint8_t *op;
107    
108     /* it must be either a BOOTREQUEST or a BOOTREPLY */
109     if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
110     return -1;
111     /* get message type option */
112     op = get_option(p, DHCP_MESSAGE_TYPE);
113     if (op != NULL)
114     return op[0];
115     return -1;
116     }
117    
118     /**
119     * signal_handler - handles signals ;-)
120     * sig - sent signal
121     */
122     static int dhcprelay_stopflag;
123     static void dhcprelay_signal_handler(int sig)
124     {
125     dhcprelay_stopflag = 1;
126     }
127    
128     /**
129     * get_client_devices - parses the devices list
130     * dev_list - comma separated list of devices
131     * returns array
132     */
133     static char ** get_client_devices(char *dev_list, int *client_number)
134     {
135     char *s, *list, **client_dev;
136     int i, cn;
137    
138     /* copy list */
139     list = xstrdup(dev_list);
140     if (list == NULL) return NULL;
141    
142     /* get number of items */
143     for (s = dev_list, cn = 1; *s; s++)
144     if (*s == ',')
145     cn++;
146    
147     client_dev = xzalloc(cn * sizeof(*client_dev));
148    
149     /* parse list */
150     s = strtok(list, ",");
151     i = 0;
152     while (s != NULL) {
153     client_dev[i++] = xstrdup(s);
154     s = strtok(NULL, ",");
155     }
156    
157     /* free copy and exit */
158     free(list);
159     *client_number = cn;
160     return client_dev;
161     }
162    
163    
164     /* Creates listen sockets (in fds) and returns the number allocated. */
165     static int init_sockets(char **client, int num_clients,
166     char *server, int *fds, int *max_socket)
167     {
168     int i;
169    
170     /* talk to real server on bootps */
171     fds[0] = listen_socket(htonl(INADDR_ANY), 67, server);
172     *max_socket = fds[0];
173    
174     /* array starts at 1 since server is 0 */
175     num_clients++;
176    
177     for (i=1; i < num_clients; i++) {
178     /* listen for clients on bootps */
179     fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]);
180     if (fds[i] > *max_socket) *max_socket = fds[i];
181     }
182    
183     return i;
184     }
185    
186    
187     /**
188     * pass_on() - forwards dhcp packets from client to server
189     * p - packet to send
190     * client - number of the client
191     */
192     static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
193     struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
194     {
195     int res, type;
196     struct xid_item *item;
197    
198     /* check packet_type */
199     type = get_dhcp_packet_type(p);
200     if (type != DHCPDISCOVER && type != DHCPREQUEST
201     && type != DHCPDECLINE && type != DHCPRELEASE
202     && type != DHCPINFORM
203     ) {
204     return;
205     }
206    
207     /* create new xid entry */
208     item = xid_add(p->xid, client_addr, client);
209    
210     /* forward request to LAN (server) */
211     res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
212     sizeof(struct sockaddr_in));
213     if (res != packet_len) {
214     bb_perror_msg("pass_on");
215     return;
216     }
217     }
218    
219     /**
220     * pass_back() - forwards dhcp packets from server to client
221     * p - packet to send
222     */
223     static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
224     {
225     int res, type;
226     struct xid_item *item;
227    
228     /* check xid */
229     item = xid_find(p->xid);
230     if (!item) {
231     return;
232     }
233    
234     /* check packet type */
235     type = get_dhcp_packet_type(p);
236     if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
237     return;
238     }
239    
240     if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
241     item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
242     if (item->client > MAX_INTERFACES)
243     return;
244     res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
245     sizeof(item->ip));
246     if (res != packet_len) {
247     bb_perror_msg("pass_back");
248     return;
249     }
250    
251     /* remove xid entry */
252     xid_del(p->xid);
253     }
254    
255     static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
256     struct sockaddr_in *server_addr, uint32_t gw_ip)
257     {
258     struct dhcpMessage dhcp_msg;
259     fd_set rfds;
260     size_t packlen;
261     socklen_t addr_size;
262     struct sockaddr_in client_addr;
263     struct timeval tv;
264     int i;
265    
266     while (!dhcprelay_stopflag) {
267     FD_ZERO(&rfds);
268     for (i = 0; i < num_sockets; i++)
269     FD_SET(fds[i], &rfds);
270     tv.tv_sec = SELECT_TIMEOUT;
271     tv.tv_usec = 0;
272     if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
273     /* server */
274     if (FD_ISSET(fds[0], &rfds)) {
275     packlen = udhcp_get_packet(&dhcp_msg, fds[0]);
276     if (packlen > 0) {
277     pass_back(&dhcp_msg, packlen, fds);
278     }
279     }
280     for (i = 1; i < num_sockets; i++) {
281     /* clients */
282     if (!FD_ISSET(fds[i], &rfds))
283     continue;
284     addr_size = sizeof(struct sockaddr_in);
285     packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
286     (struct sockaddr *)(&client_addr), &addr_size);
287     if (packlen <= 0)
288     continue;
289     if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0)
290     dhcp_msg.giaddr = gw_ip;
291     pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
292     }
293     }
294     xid_expire();
295     }
296     }
297    
298     int dhcprelay_main(int argc, char **argv)
299     {
300     int i, num_sockets, max_socket, fds[MAX_INTERFACES];
301     uint32_t gw_ip;
302     char **clients;
303     struct sockaddr_in server_addr;
304    
305     server_addr.sin_family = AF_INET;
306     server_addr.sin_port = htons(67);
307     if (argc == 4) {
308     if (!inet_aton(argv[3], &server_addr.sin_addr))
309     bb_perror_msg_and_die("didn't grok server");
310     } else if (argc == 3) {
311     server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
312     } else {
313     bb_show_usage();
314     }
315     clients = get_client_devices(argv[1], &num_sockets);
316     if (!clients) return 0;
317    
318     signal(SIGTERM, dhcprelay_signal_handler);
319     signal(SIGQUIT, dhcprelay_signal_handler);
320     signal(SIGINT, dhcprelay_signal_handler);
321    
322     num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket);
323    
324     if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1)
325     return 1;
326    
327     dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
328    
329     if (ENABLE_FEATURE_CLEAN_UP) {
330     for (i = 0; i < num_sockets; i++) {
331     close(fds[i]);
332     free(clients[i]);
333     }
334     }
335    
336     return 0;
337     }