# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / udhcp / packet.c
1 #include <unistd.h>
2 #include <string.h>
3 #include <netinet/in.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <features.h>
7 #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
8 #include <netpacket/packet.h>
9 #include <net/ethernet.h>
10 #else
11 #include <asm/types.h>
12 #include <linux/if_packet.h>
13 #include <linux/if_ether.h>
14 #endif
15 #include <errno.h>
16
17 #include "packet.h"
18 #include "debug.h"
19 #include "dhcpd.h"
20 #include "options.h"
21
22 /* read a packet from socket fd */
23 int get_packet(struct dhcpMessage *packet, int fd)
24 {
25         int bytes;
26         int i;
27         char broken_vendors[][8] = {
28                 "MSFT 98",
29                 ""
30         };
31         char *vendor;
32
33         memset(packet, 0, sizeof(struct dhcpMessage));
34         bytes = read(fd, packet, sizeof(struct dhcpMessage));
35         if (bytes < 0) {
36                 DEBUG(LOG_INFO, "couldn't read on listening socket -- ignoring");
37                 return -1;
38         }
39
40         if (ntohl(packet->cookie) != DHCP_MAGIC) {
41                 LOG(LOG_ERR, "received bogus message -- ignoring");
42                 return -1;
43         }
44         DEBUG(LOG_INFO, "oooooh!!! got some!");
45         
46         if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
47                 for (i = 0; broken_vendors[i][0]; i++) {
48                         if (vendor[OPT_LEN - 2] == (signed) strlen(broken_vendors[i]) &&
49                             !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2]) &&
50                             !(ntohs(packet->flags) & BROADCAST_FLAG)) {
51                                 DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
52                                         broken_vendors[i]);
53                                 packet->flags |= htons(BROADCAST_FLAG);
54                         }
55                 }
56         }
57                                 
58
59         return bytes;
60 }
61
62
63 /* Calculate the length of a packet, and make sure its a multiple of 2 */
64 static int calc_length(struct dhcpMessage *payload)
65 {
66         int payload_length;
67         
68         payload_length = sizeof(struct dhcpMessage) - 308;
69         payload_length += end_option(payload->options) + 1;
70         if (payload_length % 2) {
71                 payload_length++;
72                 *((char *) payload + payload_length - 1) = '\0';
73         }
74
75         DEBUG(LOG_INFO, "payload length is %d bytes", payload_length);
76         
77         return payload_length;
78 }
79
80
81 u_int16_t checksum(void *addr, int count)
82 {
83         /* Compute Internet Checksum for "count" bytes
84          *         beginning at location "addr".
85          */
86         register int32_t sum = 0;
87         u_int16_t *source = (u_int16_t *) addr;
88
89         while( count > 1 )  {
90                 /*  This is the inner loop */
91                 sum += *source++;
92                 count -= 2;
93         }
94
95         /*  Add left-over byte, if any */
96         if( count > 0 )
97                 sum += * (unsigned char *) source;
98
99         /*  Fold 32-bit sum to 16 bits */
100         while (sum>>16)
101                 sum = (sum & 0xffff) + (sum >> 16);
102
103         return ~sum;
104 }
105
106
107 /* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */
108 int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
109                    u_int32_t dest_ip, int dest_port, char *dest_arp, int ifindex)
110 {
111         int fd;
112         int result, payload_length = calc_length(payload);
113         struct sockaddr_ll dest;
114         struct udp_dhcp_packet packet;
115
116         if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
117                 DEBUG(LOG_ERR, "socket call failed: %s", sys_errlist[errno]);
118                 return -1;
119         }
120         
121         memset(&dest, 0, sizeof(dest));
122         memset(&packet, 0, sizeof(packet));
123         
124         dest.sll_family = AF_PACKET;
125         dest.sll_protocol = htons(ETH_P_IP);
126         dest.sll_ifindex = ifindex;
127         dest.sll_halen = 6;
128         memcpy(dest.sll_addr, dest_arp, 6);
129         if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
130                 DEBUG(LOG_ERR, "bind call failed: %s", sys_errlist[errno]);
131                 close(fd);
132                 return -1;
133         }
134
135         packet.ip.protocol = IPPROTO_UDP;
136         packet.ip.saddr = source_ip;
137         packet.ip.daddr = dest_ip;
138         packet.ip.tot_len = htons(sizeof(packet.udp) + payload_length); /* cheat on the psuedo-header */
139         packet.udp.source = htons(source_port);
140         packet.udp.dest = htons(dest_port);
141         packet.udp.len = htons(sizeof(packet.udp) + payload_length);
142         memcpy(&(packet.data), payload, payload_length);
143         packet.udp.check = checksum(&packet, sizeof(packet.ip) + sizeof(packet.udp) + payload_length);
144         
145         packet.ip.tot_len = htons(sizeof(packet.ip) + sizeof(packet.udp) + payload_length);
146         packet.ip.ihl = sizeof(packet.ip) >> 2;
147         packet.ip.version = IPVERSION;
148         packet.ip.ttl = IPDEFTTL;
149         packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
150
151         result = sendto(fd, &packet, ntohs(packet.ip.tot_len), 0, (struct sockaddr *) &dest, sizeof(dest));
152         if (result <= 0) {
153                 DEBUG(LOG_ERR, "write on socket failed: %s", sys_errlist[errno]);
154         }
155         close(fd);
156         return result;
157 }
158
159
160 /* Let the kernel do all the work for packet generation */
161 int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
162                    u_int32_t dest_ip, int dest_port)
163 {
164         int n = 1;
165         int fd, result, payload_length = calc_length(payload);
166         struct sockaddr_in client;
167         
168         if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
169                 return -1;
170         
171         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
172                 return -1;
173
174         memset(&client, 0, sizeof(client));
175         client.sin_family = AF_INET;
176         client.sin_port = htons(source_port);
177         client.sin_addr.s_addr = source_ip;
178
179         if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
180                 return -1;
181
182         memset(&client, 0, sizeof(client));
183         client.sin_family = AF_INET;
184         client.sin_port = htons(dest_port);
185         client.sin_addr.s_addr = dest_ip; 
186
187         if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
188                 return -1;
189
190         result = write(fd, payload, payload_length);
191         close(fd);
192         return result;
193 }       
194