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