xref: /OK3568_Linux_fs/app/forlinx/quectelCM/libmnl/dhcp/packet.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright 2008, The Android Open Source Project
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Licensed under the Apache License, Version 2.0 (the "License");
5*4882a593Smuzhiyun  * you may not use this file except in compliance with the License.
6*4882a593Smuzhiyun  * You may obtain a copy of the License at
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *     http://www.apache.org/licenses/LICENSE-2.0
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * Unless required by applicable law or agreed to in writing, software
11*4882a593Smuzhiyun  * distributed under the License is distributed on an "AS IS" BASIS,
12*4882a593Smuzhiyun  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*4882a593Smuzhiyun  * See the License for the specific language governing permissions and
14*4882a593Smuzhiyun  * limitations under the License.
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <errno.h>
18*4882a593Smuzhiyun #include <stdlib.h>
19*4882a593Smuzhiyun #include <string.h>
20*4882a593Smuzhiyun #include <sys/socket.h>
21*4882a593Smuzhiyun #include <sys/uio.h>
22*4882a593Smuzhiyun #include <linux/if_ether.h>
23*4882a593Smuzhiyun #include <linux/if_packet.h>
24*4882a593Smuzhiyun #include <netinet/in.h>
25*4882a593Smuzhiyun #include <netinet/ip.h>
26*4882a593Smuzhiyun #include <netinet/udp.h>
27*4882a593Smuzhiyun #include <unistd.h>
28*4882a593Smuzhiyun #include <stdio.h>
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include "dhcpmsg.h"
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun int fatal();
33*4882a593Smuzhiyun 
open_raw_socket(const char * ifname,uint8_t * hwaddr,int if_index)34*4882a593Smuzhiyun int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun     int s;
37*4882a593Smuzhiyun     struct sockaddr_ll bindaddr;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun     if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
40*4882a593Smuzhiyun         return fatal("socket(PF_PACKET)");
41*4882a593Smuzhiyun     }
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun     memset(&bindaddr, 0, sizeof(bindaddr));
44*4882a593Smuzhiyun     bindaddr.sll_family = AF_PACKET;
45*4882a593Smuzhiyun     bindaddr.sll_protocol = htons(ETH_P_IP);
46*4882a593Smuzhiyun     bindaddr.sll_halen = ETH_ALEN;
47*4882a593Smuzhiyun     memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
48*4882a593Smuzhiyun     bindaddr.sll_ifindex = if_index;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun     if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
51*4882a593Smuzhiyun         return fatal("Cannot bind raw socket to interface");
52*4882a593Smuzhiyun     }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun     return s;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
checksum(void * buffer,unsigned int count,uint32_t startsum)57*4882a593Smuzhiyun static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun     uint16_t *up = (uint16_t *)buffer;
60*4882a593Smuzhiyun     uint32_t sum = startsum;
61*4882a593Smuzhiyun     uint32_t upper16;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun     while (count > 1) {
64*4882a593Smuzhiyun         sum += *up++;
65*4882a593Smuzhiyun         count -= 2;
66*4882a593Smuzhiyun     }
67*4882a593Smuzhiyun     if (count > 0) {
68*4882a593Smuzhiyun         sum += (uint16_t) *(uint8_t *)up;
69*4882a593Smuzhiyun     }
70*4882a593Smuzhiyun     while ((upper16 = (sum >> 16)) != 0) {
71*4882a593Smuzhiyun         sum = (sum & 0xffff) + upper16;
72*4882a593Smuzhiyun     }
73*4882a593Smuzhiyun     return sum;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
finish_sum(uint32_t sum)76*4882a593Smuzhiyun static uint32_t finish_sum(uint32_t sum)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun     return ~sum & 0xffff;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
send_packet(int s,int if_index,struct dhcp_msg * msg,int size,uint32_t saddr,uint32_t daddr,uint32_t sport,uint32_t dport)81*4882a593Smuzhiyun int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
82*4882a593Smuzhiyun                 uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun     struct iphdr ip;
85*4882a593Smuzhiyun     struct udphdr udp;
86*4882a593Smuzhiyun     struct iovec iov[3];
87*4882a593Smuzhiyun     uint32_t udpsum;
88*4882a593Smuzhiyun     uint16_t temp;
89*4882a593Smuzhiyun     struct msghdr msghdr;
90*4882a593Smuzhiyun     struct sockaddr_ll destaddr;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun     ip.version = IPVERSION;
93*4882a593Smuzhiyun     ip.ihl = sizeof(ip) >> 2;
94*4882a593Smuzhiyun     ip.tos = 0;
95*4882a593Smuzhiyun     ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
96*4882a593Smuzhiyun     ip.id = 0;
97*4882a593Smuzhiyun     ip.frag_off = 0;
98*4882a593Smuzhiyun     ip.ttl = IPDEFTTL;
99*4882a593Smuzhiyun     ip.protocol = IPPROTO_UDP;
100*4882a593Smuzhiyun     ip.check = 0;
101*4882a593Smuzhiyun     ip.saddr = saddr;
102*4882a593Smuzhiyun     ip.daddr = daddr;
103*4882a593Smuzhiyun     ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun     udp.source = htons(sport);
106*4882a593Smuzhiyun     udp.dest = htons(dport);
107*4882a593Smuzhiyun     udp.len = htons(sizeof(udp) + size);
108*4882a593Smuzhiyun     udp.check = 0;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun     /* Calculate checksum for pseudo header */
111*4882a593Smuzhiyun     udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
112*4882a593Smuzhiyun     udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
113*4882a593Smuzhiyun     temp = htons(IPPROTO_UDP);
114*4882a593Smuzhiyun     udpsum = checksum(&temp, sizeof(temp), udpsum);
115*4882a593Smuzhiyun     temp = udp.len;
116*4882a593Smuzhiyun     udpsum = checksum(&temp, sizeof(temp), udpsum);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun     /* Add in the checksum for the udp header */
119*4882a593Smuzhiyun     udpsum = checksum(&udp, sizeof(udp), udpsum);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun     /* Add in the checksum for the data */
122*4882a593Smuzhiyun     udpsum = checksum(msg, size, udpsum);
123*4882a593Smuzhiyun     udp.check = finish_sum(udpsum);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun     iov[0].iov_base = (char *)&ip;
126*4882a593Smuzhiyun     iov[0].iov_len = sizeof(ip);
127*4882a593Smuzhiyun     iov[1].iov_base = (char *)&udp;
128*4882a593Smuzhiyun     iov[1].iov_len = sizeof(udp);
129*4882a593Smuzhiyun     iov[2].iov_base = (char *)msg;
130*4882a593Smuzhiyun     iov[2].iov_len = size;
131*4882a593Smuzhiyun     memset(&destaddr, 0, sizeof(destaddr));
132*4882a593Smuzhiyun     destaddr.sll_family = AF_PACKET;
133*4882a593Smuzhiyun     destaddr.sll_protocol = htons(ETH_P_IP);
134*4882a593Smuzhiyun     destaddr.sll_ifindex = if_index;
135*4882a593Smuzhiyun     destaddr.sll_halen = ETH_ALEN;
136*4882a593Smuzhiyun     memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun     msghdr.msg_name = &destaddr;
139*4882a593Smuzhiyun     msghdr.msg_namelen = sizeof(destaddr);
140*4882a593Smuzhiyun     msghdr.msg_iov = iov;
141*4882a593Smuzhiyun     msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
142*4882a593Smuzhiyun     msghdr.msg_flags = 0;
143*4882a593Smuzhiyun     msghdr.msg_control = 0;
144*4882a593Smuzhiyun     msghdr.msg_controllen = 0;
145*4882a593Smuzhiyun     return sendmsg(s, &msghdr, 0);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
receive_packet(int s,struct dhcp_msg * msg)148*4882a593Smuzhiyun int receive_packet(int s, struct dhcp_msg *msg)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun     int nread;
151*4882a593Smuzhiyun     int is_valid;
152*4882a593Smuzhiyun     struct dhcp_packet {
153*4882a593Smuzhiyun         struct iphdr ip;
154*4882a593Smuzhiyun         struct udphdr udp;
155*4882a593Smuzhiyun         struct dhcp_msg dhcp;
156*4882a593Smuzhiyun     } packet;
157*4882a593Smuzhiyun     int dhcp_size;
158*4882a593Smuzhiyun     uint32_t sum;
159*4882a593Smuzhiyun     uint16_t temp;
160*4882a593Smuzhiyun     uint32_t saddr, daddr;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun     nread = read(s, &packet, sizeof(packet));
163*4882a593Smuzhiyun     if (nread < 0) {
164*4882a593Smuzhiyun         return -1;
165*4882a593Smuzhiyun     }
166*4882a593Smuzhiyun     /*
167*4882a593Smuzhiyun      * The raw packet interface gives us all packets received by the
168*4882a593Smuzhiyun      * network interface. We need to filter out all packets that are
169*4882a593Smuzhiyun      * not meant for us.
170*4882a593Smuzhiyun      */
171*4882a593Smuzhiyun     is_valid = 0;
172*4882a593Smuzhiyun     if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
173*4882a593Smuzhiyun #if VERBOSE
174*4882a593Smuzhiyun         ALOGD("Packet is too small (%d) to be a UDP datagram", nread);
175*4882a593Smuzhiyun #endif
176*4882a593Smuzhiyun     } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
177*4882a593Smuzhiyun #if VERBOSE
178*4882a593Smuzhiyun         ALOGD("Not a valid IP packet");
179*4882a593Smuzhiyun #endif
180*4882a593Smuzhiyun     } else if (nread < ntohs(packet.ip.tot_len)) {
181*4882a593Smuzhiyun #if VERBOSE
182*4882a593Smuzhiyun         ALOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
183*4882a593Smuzhiyun #endif
184*4882a593Smuzhiyun     } else if (packet.ip.protocol != IPPROTO_UDP) {
185*4882a593Smuzhiyun #if VERBOSE
186*4882a593Smuzhiyun         ALOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
187*4882a593Smuzhiyun #endif
188*4882a593Smuzhiyun     } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
189*4882a593Smuzhiyun #if VERBOSE
190*4882a593Smuzhiyun         ALOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
191*4882a593Smuzhiyun #endif
192*4882a593Smuzhiyun     } else {
193*4882a593Smuzhiyun         is_valid = 1;
194*4882a593Smuzhiyun     }
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun     if (!is_valid) {
197*4882a593Smuzhiyun         return -1;
198*4882a593Smuzhiyun     }
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun     /* Seems like it's probably a valid DHCP packet */
201*4882a593Smuzhiyun     /* validate IP header checksum */
202*4882a593Smuzhiyun     sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
203*4882a593Smuzhiyun     if (sum != 0) {
204*4882a593Smuzhiyun         printf("IP header checksum failure (0x%x)\n", packet.ip.check);
205*4882a593Smuzhiyun         return -1;
206*4882a593Smuzhiyun     }
207*4882a593Smuzhiyun     /*
208*4882a593Smuzhiyun      * Validate the UDP checksum.
209*4882a593Smuzhiyun      * Since we don't need the IP header anymore, we "borrow" it
210*4882a593Smuzhiyun      * to construct the pseudo header used in the checksum calculation.
211*4882a593Smuzhiyun      */
212*4882a593Smuzhiyun     dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
213*4882a593Smuzhiyun     /*
214*4882a593Smuzhiyun      * check validity of dhcp_size.
215*4882a593Smuzhiyun      * 1) cannot be negative or zero.
216*4882a593Smuzhiyun      * 2) src buffer contains enough bytes to copy
217*4882a593Smuzhiyun      * 3) cannot exceed destination buffer
218*4882a593Smuzhiyun      */
219*4882a593Smuzhiyun     if ((dhcp_size <= 0) ||
220*4882a593Smuzhiyun         ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
221*4882a593Smuzhiyun         ((int)sizeof(struct dhcp_msg) < dhcp_size)) {
222*4882a593Smuzhiyun #if VERBOSE
223*4882a593Smuzhiyun         printf("Malformed Packet\n");
224*4882a593Smuzhiyun #endif
225*4882a593Smuzhiyun         return -1;
226*4882a593Smuzhiyun     }
227*4882a593Smuzhiyun     saddr = packet.ip.saddr;
228*4882a593Smuzhiyun     daddr = packet.ip.daddr;
229*4882a593Smuzhiyun     nread = ntohs(packet.ip.tot_len);
230*4882a593Smuzhiyun     memset(&packet.ip, 0, sizeof(packet.ip));
231*4882a593Smuzhiyun     packet.ip.saddr = saddr;
232*4882a593Smuzhiyun     packet.ip.daddr = daddr;
233*4882a593Smuzhiyun     packet.ip.protocol = IPPROTO_UDP;
234*4882a593Smuzhiyun     packet.ip.tot_len = packet.udp.len;
235*4882a593Smuzhiyun     temp = packet.udp.check;
236*4882a593Smuzhiyun     packet.udp.check = 0;
237*4882a593Smuzhiyun     sum = finish_sum(checksum(&packet, nread, 0));
238*4882a593Smuzhiyun     packet.udp.check = temp;
239*4882a593Smuzhiyun     if (!sum)
240*4882a593Smuzhiyun         sum = finish_sum(sum);
241*4882a593Smuzhiyun     if (temp != sum) {
242*4882a593Smuzhiyun         printf("UDP header checksum failure (0x%x should be 0x%x)\n", sum, temp);
243*4882a593Smuzhiyun         return -1;
244*4882a593Smuzhiyun     }
245*4882a593Smuzhiyun     memcpy(msg, &packet.dhcp, dhcp_size);
246*4882a593Smuzhiyun     return dhcp_size;
247*4882a593Smuzhiyun }
248