remove trailing whitespace
[oweals/busybox.git] / networking / udhcp / packet.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * packet.c -- packet ops
4  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
5  *
6  * Licensed under GPLv2, see file LICENSE in this tarball for details.
7  */
8 #include <netinet/in.h>
9 #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
10 #include <netpacket/packet.h>
11 #include <net/ethernet.h>
12 #else
13 #include <asm/types.h>
14 #include <linux/if_packet.h>
15 #include <linux/if_ether.h>
16 #endif
17
18 #include "common.h"
19 #include "dhcpd.h"
20 #include "options.h"
21
22 void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
23 {
24         memset(packet, 0, sizeof(struct dhcp_packet));
25         packet->op = BOOTREQUEST; /* if client to a server */
26         switch (type) {
27         case DHCPOFFER:
28         case DHCPACK:
29         case DHCPNAK:
30                 packet->op = BOOTREPLY; /* if server to client */
31         }
32         packet->htype = ETH_10MB;
33         packet->hlen = ETH_10MB_LEN;
34         packet->cookie = htonl(DHCP_MAGIC);
35         packet->options[0] = DHCP_END;
36         add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
37 }
38
39 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
40 void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
41 {
42         char buf[sizeof(packet->chaddr)*2 + 1];
43
44         if (dhcp_verbose < 2)
45                 return;
46
47         bb_info_msg(
48                 //" op %x"
49                 //" htype %x"
50                 " hlen %x"
51                 //" hops %x"
52                 " xid %x"
53                 //" secs %x"
54                 //" flags %x"
55                 " ciaddr %x"
56                 " yiaddr %x"
57                 " siaddr %x"
58                 " giaddr %x"
59                 //" chaddr %s"
60                 //" sname %s"
61                 //" file %s"
62                 //" cookie %x"
63                 //" options %s"
64                 //, packet->op
65                 //, packet->htype
66                 , packet->hlen
67                 //, packet->hops
68                 , packet->xid
69                 //, packet->secs
70                 //, packet->flags
71                 , packet->ciaddr
72                 , packet->yiaddr
73                 , packet->siaddr_nip
74                 , packet->gateway_nip
75                 //, packet->chaddr[16]
76                 //, packet->sname[64]
77                 //, packet->file[128]
78                 //, packet->cookie
79                 //, packet->options[]
80         );
81         *bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
82         bb_info_msg(" chaddr %s", buf);
83 }
84 #endif
85
86 /* Read a packet from socket fd, return -1 on read error, -2 on packet error */
87 int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
88 {
89         int bytes;
90         unsigned char *vendor;
91
92         memset(packet, 0, sizeof(*packet));
93         bytes = safe_read(fd, packet, sizeof(*packet));
94         if (bytes < 0) {
95                 log1("Packet read error, ignoring");
96                 return bytes; /* returns -1 */
97         }
98
99         if (packet->cookie != htonl(DHCP_MAGIC)) {
100                 bb_info_msg("Packet with bad magic, ignoring");
101                 return -2;
102         }
103         log1("Received a packet");
104         udhcp_dump_packet(packet);
105
106         if (packet->op == BOOTREQUEST) {
107                 vendor = get_option(packet, DHCP_VENDOR);
108                 if (vendor) {
109 #if 0
110                         static const char broken_vendors[][8] = {
111                                 "MSFT 98",
112                                 ""
113                         };
114                         int i;
115                         for (i = 0; broken_vendors[i][0]; i++) {
116                                 if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i])
117                                  && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2])
118                                 ) {
119                                         log1("Broken client (%s), forcing broadcast replies",
120                                                 broken_vendors[i]);
121                                         packet->flags |= htons(BROADCAST_FLAG);
122                                 }
123                         }
124 #else
125                         if (vendor[OPT_LEN - 2] == (uint8_t)(sizeof("MSFT 98")-1)
126                          && memcmp(vendor, "MSFT 98", sizeof("MSFT 98")-1) == 0
127                         ) {
128                                 log1("Broken client (%s), forcing broadcast replies", "MSFT 98");
129                                 packet->flags |= htons(BROADCAST_FLAG);
130                         }
131 #endif
132                 }
133         }
134
135         return bytes;
136 }
137
138 uint16_t FAST_FUNC udhcp_checksum(void *addr, int count)
139 {
140         /* Compute Internet Checksum for "count" bytes
141          * beginning at location "addr".
142          */
143         int32_t sum = 0;
144         uint16_t *source = (uint16_t *) addr;
145
146         while (count > 1)  {
147                 /*  This is the inner loop */
148                 sum += *source++;
149                 count -= 2;
150         }
151
152         /*  Add left-over byte, if any */
153         if (count > 0) {
154                 /* Make sure that the left-over byte is added correctly both
155                  * with little and big endian hosts */
156                 uint16_t tmp = 0;
157                 *(uint8_t*)&tmp = *(uint8_t*)source;
158                 sum += tmp;
159         }
160         /*  Fold 32-bit sum to 16 bits */
161         while (sum >> 16)
162                 sum = (sum & 0xffff) + (sum >> 16);
163
164         return ~sum;
165 }
166
167 /* Construct a ip/udp header for a packet, send packet */
168 int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
169                 uint32_t source_ip, int source_port,
170                 uint32_t dest_ip, int dest_port, const uint8_t *dest_arp,
171                 int ifindex)
172 {
173         struct sockaddr_ll dest;
174         struct ip_udp_dhcp_packet packet;
175         int fd;
176         int result = -1;
177         const char *msg;
178
179         enum {
180                 IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
181                 UPD_DHCP_SIZE    = IP_UPD_DHCP_SIZE - offsetof(struct ip_udp_dhcp_packet, udp),
182         };
183
184         fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
185         if (fd < 0) {
186                 msg = "socket(%s)";
187                 goto ret_msg;
188         }
189
190         memset(&dest, 0, sizeof(dest));
191         memset(&packet, 0, sizeof(packet));
192         packet.data = *dhcp_pkt; /* struct copy */
193
194         dest.sll_family = AF_PACKET;
195         dest.sll_protocol = htons(ETH_P_IP);
196         dest.sll_ifindex = ifindex;
197         dest.sll_halen = 6;
198         memcpy(dest.sll_addr, dest_arp, 6);
199         if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
200                 msg = "bind(%s)";
201                 goto ret_close;
202         }
203
204         packet.ip.protocol = IPPROTO_UDP;
205         packet.ip.saddr = source_ip;
206         packet.ip.daddr = dest_ip;
207         packet.udp.source = htons(source_port);
208         packet.udp.dest = htons(dest_port);
209         /* size, excluding IP header: */
210         packet.udp.len = htons(UPD_DHCP_SIZE);
211         /* for UDP checksumming, ip.len is set to UDP packet len */
212         packet.ip.tot_len = packet.udp.len;
213         packet.udp.check = udhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
214         /* but for sending, it is set to IP packet len */
215         packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
216         packet.ip.ihl = sizeof(packet.ip) >> 2;
217         packet.ip.version = IPVERSION;
218         packet.ip.ttl = IPDEFTTL;
219         packet.ip.check = udhcp_checksum(&packet.ip, sizeof(packet.ip));
220
221         /* Currently we send full-sized DHCP packets (zero padded).
222          * If you need to change this: last byte of the packet is
223          * packet.data.options[end_option(packet.data.options)]
224          */
225         udhcp_dump_packet(dhcp_pkt);
226         result = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
227                                 (struct sockaddr *) &dest, sizeof(dest));
228         msg = "sendto";
229  ret_close:
230         close(fd);
231         if (result < 0) {
232  ret_msg:
233                 bb_perror_msg(msg, "PACKET");
234         }
235         return result;
236 }
237
238 /* Let the kernel do all the work for packet generation */
239 int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
240                 uint32_t source_ip, int source_port,
241                 uint32_t dest_ip, int dest_port)
242 {
243         struct sockaddr_in client;
244         int fd;
245         int result = -1;
246         const char *msg;
247
248         enum {
249                 DHCP_SIZE = sizeof(struct dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
250         };
251
252         fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
253         if (fd < 0) {
254                 msg = "socket(%s)";
255                 goto ret_msg;
256         }
257         setsockopt_reuseaddr(fd);
258
259         memset(&client, 0, sizeof(client));
260         client.sin_family = AF_INET;
261         client.sin_port = htons(source_port);
262         client.sin_addr.s_addr = source_ip;
263         if (bind(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
264                 msg = "bind(%s)";
265                 goto ret_close;
266         }
267
268         memset(&client, 0, sizeof(client));
269         client.sin_family = AF_INET;
270         client.sin_port = htons(dest_port);
271         client.sin_addr.s_addr = dest_ip;
272         if (connect(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
273                 msg = "connect";
274                 goto ret_close;
275         }
276
277         /* Currently we send full-sized DHCP packets (see above) */
278         udhcp_dump_packet(dhcp_pkt);
279         result = safe_write(fd, dhcp_pkt, DHCP_SIZE);
280         msg = "write";
281  ret_close:
282         close(fd);
283         if (result < 0) {
284  ret_msg:
285                 bb_perror_msg(msg, "UDP");
286         }
287         return result;
288 }