]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dhcpmessage.cc
2 * Copyright 2006, 2007 Stefan Rompf <sux@loplof.de>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2 as published by the Free Software Foundation
12 #include <sys/types.h>
13 #include <sys/socket.h>
15 #include <arpa/inet.h>
16 #include <netpacket/packet.h>
17 #include <net/ethernet.h>
18 #include <netinet/ip.h>
19 #include <netinet/udp.h>
20 #include <netinet/ether.h>
22 #include "dhcpmessage.hh"
24 static const unsigned char vendcookie
[] = { 99, 130, 83, 99 };
25 void dm_init(struct dhcp_message
*msg
) {
26 memset(msg
, 0, sizeof(*msg
));
27 msg
->pos
= msg
->options
+4;
28 // msg->flags = htons(0x8000);
29 memcpy(msg
->options
, vendcookie
, 4);
32 void dm_add_option(struct dhcp_message
*msg
, u_int8_t option
,
33 u_int8_t length
, void *opt
) {
34 u_int8_t
*pos
= msg
->pos
;
36 if (&msg
->options
[MAX_OPT_LEN
] - pos
< length
+ 2) abort();
40 memcpy(pos
, opt
, length
);
47 void dm_finish_options(struct dhcp_message
*msg
) {
48 if (msg
->pos
== &msg
->options
[MAX_OPT_LEN
]) abort();
54 u_int8_t
*dm_next_option(struct dhcp_message
*msg
) {
55 u_int8_t
*pos
= msg
->pos
;
59 if (pos
>= msg
->last
) return NULL
;
61 /* skip pad packets */
62 while(!*pos
) if (++pos
>= msg
->last
) return NULL
;
64 /* End of option marker */
66 /* Overload option handling */
67 if (msg
->currentblock
< msg
->overload
) { // currentblock: 0,1,3
68 msg
->currentblock
= (dhcp_overload_opts
)(msg
->currentblock
+1);
69 if (msg
->overload
& DHCP_OVERLOAD_FILE
& msg
->currentblock
) {
71 msg
->last
= &msg
->file
[128];
72 } else { // SNAME or BOTH
74 msg
->last
= &msg
->sname
[64];
75 msg
->currentblock
= DHCP_OVERLOAD_BOTH
; // definitely last block
77 /* skip pad packets */
78 while(!*pos
) if (++pos
>= msg
->last
) return NULL
;
84 /* Actually, this is extra paranoia. Even if pos+1
85 * leaves the dhcp_message structure, the next
86 * check would catch this as long as we don't
87 * try to access an unmapped page ;-)
89 if (pos
+1 >= msg
->last
) return NULL
;
93 if (pos
+ length
+ 2 > msg
->last
) return NULL
;
95 msg
->pos
= pos
+ length
+2;
101 DHCPCommunicator::DHCPCommunicator(const std::string
& remoteAddr
)
103 ComboAddress
remote(remoteAddr
, 67); // 195.241.76.195, 82.169.27.254
104 d_socket
=socket(AF_INET
, SOCK_DGRAM
, 0);
107 if(setsockopt(d_socket
, SOL_SOCKET
,SO_REUSEADDR
,(char*)&tmp
,sizeof tmp
)<0)
108 unixDie("Setting reuse flag");
110 ComboAddress
local("0.0.0.0", 67);
112 if(bind(d_socket
, (struct sockaddr
*)&local
, local
.getSocklen()) < 0)
115 if(connect(d_socket
, (struct sockaddr
*)&remote
, remote
.getSocklen()) < 0)
116 unixDie("connecting");
120 string
DHCPCommunicator::getMac(const std::string
& ip
)
122 struct dhcp_message msg
;
126 msg
.op
= BOOTP_OPCODE_REQUEST
;
129 memset(&msg
.chaddr
, 0, sizeof(msg
.chaddr
));
131 ComboAddress
about(ip
);
133 msg
.ciaddr
= about
.sin4
.sin_addr
.s_addr
; // 0x0c01000a; // 0xda837bd4; // ask about 4.3.2.1
135 struct sockaddr_in local
;
136 socklen_t locallen
=sizeof(local
);
137 getsockname(d_socket
, (struct sockaddr
*)&local
, &locallen
);
139 msg
.giaddr
= local
.sin_addr
.s_addr
; // 0x0c01000a; // 0xda837bd4; // or IP is also 4.3.2.1 (it ain't)
141 int dhcptype
= 10; // 13; // DHCP_TYPE_LEASEQUERY cisco style
142 dm_add_option(&msg
, DHCP_OPTION_TYPE
, 1, &dhcptype
);
145 dm_add_option(&msg
, DHCP_OPTION_PARAMREQ
, 1, &dhcptype
);
148 dm_add_option(&msg
, DHCP_OPTION_PARAMREQ
, 1, &dhcptype
);
150 send(d_socket
, (char*)&msg
.op
, msg
.pos
- &msg
.op
, 0);
154 int ret
=recv(d_socket
, packet
, sizeof(packet
), 0);
156 memcpy((char*)&msg
.op
, packet
, ret
);
158 snprintf(mac
, 19, "%02x:%02x:%02x:%02x:%02x:%02x", msg
.chaddr
[0], msg
.chaddr
[1], msg
.chaddr
[2],
159 msg
.chaddr
[3], msg
.chaddr
[4], msg
.chaddr
[5]);
169 int dm_parse_msg_raw(char *dframe
, int plen
,
170 struct in_addr
*from_ip
, struct dhcp_message
*msg
) {
176 if (plen
< sizeof(*ip
)) return -1;
178 /* Verify IP: IP, UDP, ... */
179 ip
= (struct iphdr
*)dframe
;
180 iphlen
= 4 * ip
->ihl
;
181 if (ip
->version
!= 4) return -1; /* no ipv4 packet */
182 if (plen
< iphlen
|| iphlen
< 20) return -1; /* ip header too short */
183 if (plen
< ntohs(ip
->tot_len
)) return -1; /* packet too short */
184 if (in_cksum((u_short
*)ip
, iphlen
, 0)) return -1; /* checksum wrong */
185 if (ip
->protocol
!= IPPROTO_UDP
) return -1; /* no udp */
186 if (ip
->frag_off
& htons(IP_OFFMASK
)) return -1; /* won't parse fragments */
188 from_ip
->s_addr
= ip
->saddr
;
190 /* UDP src, destination */
191 udp
= (struct udphdr
*)&dframe
[iphlen
];
192 if (udp
->source
!= htons(BOOTP_SERVER_PORT
)) return -1;
193 if (udp
->dest
!= htons(BOOTP_CLIENT_PORT
)) return -1;
195 udplen
= ntohs(udp
->len
);
196 if (iphlen
+ udplen
> plen
) return -1; /* truncated BOOTPREPLY */
198 /* check udp checksum */
199 if (udp
->check
!= 0 && udp
->check
!= 0xffff) {
200 /* FIXME: checksum 0xffff has to be treated as 0. Until I've constructed
201 a testcase, treat 0xffff as no checksum */
202 /* RFC 768: Calculate the checksum including the pseudo header
203 s-ip(4), d-ip(4), 0x00(1), proto(1), udp-length(2) */
204 checksum
= htons(IPPROTO_UDP
);
205 checksum
= in_cksum((u_short
*)&udp
->len
, 2, checksum
);
206 checksum
= in_cksum((u_short
*)&ip
->saddr
, 8, ~checksum
); // ip options might follow
207 checksum
= in_cksum((u_short
*)udp
, udplen
, ~checksum
); // saddr + daddr + udp
208 if (checksum
) return -1; /* udp packet checksum wrong */
210 udplen
-= sizeof(*udp
); /* udplen is now dhcplen! */
211 if (udplen
< &msg
->options
[4] - &msg
->op
) return -1; /* BOOTPREPLY too short */
213 memcpy(&msg
->op
, &dframe
[iphlen
+sizeof(*udp
)], udplen
);
215 if (memcmp(msg
->options
, vendcookie
, 4)) return -1; /* No DHCP message */
217 msg
->pos
= msg
->options
+4;
218 msg
->last
= msg
->pos
+ (udplen
+ &msg
->options
[0] - &msg
->op
);
219 msg
->overload
= DHCP_OVERLOAD_NONE
;
220 msg
->currentblock
= DHCP_OVERLOAD_NONE
;