summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphilip <philip@FreeBSD.org>2009-10-23 14:43:17 +0000
committerphilip <philip@FreeBSD.org>2009-10-23 14:43:17 +0000
commit4cab752738fdf0b5d815b5efe533ceffbb5305fd (patch)
treed283f9cb1fc13d3a0c23f2d3c1098420badddcc2
parentad58b4ba938908e46296c7a6c7314da0d0a19ca4 (diff)
downloadFreeBSD-src-4cab752738fdf0b5d815b5efe533ceffbb5305fd.zip
FreeBSD-src-4cab752738fdf0b5d815b5efe533ceffbb5305fd.tar.gz
MFC r198352
Make dhclient use bootpc (68) as the source port for unicast DHCPREQUEST packets instead of allowing the protocol stack to pick a random source port. This fixes the behaviour where dhclient would never transition from RENEWING to BOUND without going through REBINDING in networks which are paranoid about DHCP spoofing, such as most mainstream cable-broadband ISP networks. Obtained from: OpenBSD Reviewed by: brooks Approved by: re (kib)
-rw-r--r--sbin/dhclient/bpf.c45
-rw-r--r--sbin/dhclient/dhcpd.h3
-rw-r--r--sbin/dhclient/packet.c11
3 files changed, 41 insertions, 18 deletions
diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c
index 8a669e1..9f8e45f 100644
--- a/sbin/dhclient/bpf.c
+++ b/sbin/dhclient/bpf.c
@@ -90,11 +90,23 @@ if_register_bpf(struct interface_info *info)
void
if_register_send(struct interface_info *info)
{
+ int sock, on = 1;
+
/*
* If we're using the bpf API for sending and receiving, we
* don't need to register this interface twice.
*/
info->wfdesc = info->rfdesc;
+
+ /*
+ * Use raw socket for unicast send.
+ */
+ if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
+ error("socket(SOCK_RAW): %m");
+ if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on,
+ sizeof(on)) == -1)
+ error("setsockopt(IP_HDRINCL): %m");
+ info->ufdesc = sock;
}
/*
@@ -244,35 +256,32 @@ send_packet(struct interface_info *interface, struct dhcp_packet *raw,
{
unsigned char buf[256];
struct iovec iov[2];
+ struct msghdr msg;
int result, bufp = 0;
- int sock;
-
- if (to->sin_addr.s_addr != INADDR_BROADCAST) {
- note("SENDING DIRECT");
- /* We know who the server is, send the packet via
- normal socket interface */
-
- if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) >= 0) {
- result = sendto (sock, (char *)raw, len, 0,
- (struct sockaddr *)to, sizeof *to);
- close(sock);
- if (result > 0)
- return result;
- }
- }
/* Assemble the headers... */
- assemble_hw_header(interface, buf, &bufp, hto);
+ if (to->sin_addr.s_addr == INADDR_BROADCAST)
+ assemble_hw_header(interface, buf, &bufp, hto);
assemble_udp_ip_header(buf, &bufp, from.s_addr,
to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
- /* Fire it off */
iov[0].iov_base = (char *)buf;
iov[0].iov_len = bufp;
iov[1].iov_base = (char *)raw;
iov[1].iov_len = len;
- result = writev(interface->wfdesc, iov, 2);
+ /* Fire it off */
+ if (to->sin_addr.s_addr == INADDR_BROADCAST)
+ result = writev(interface->wfdesc, iov, 2);
+ else {
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (struct sockaddr *)to;
+ msg.msg_namelen = sizeof(*to);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ result = sendmsg(interface->ufdesc, &msg, 0);
+ }
+
if (result < 0)
warning("send_packet: %m");
return (result);
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index 8097f14..bd4c9c0 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -37,6 +37,8 @@
* Enterprises. To learn more about the Internet Software Consortium,
* see ``http://www.vix.com/isc''. To learn more about Vixie
* Enterprises, see ``http://www.vix.com''.
+ *
+ * $FreeBSD$
*/
#include <sys/types.h>
@@ -194,6 +196,7 @@ struct interface_info {
char name[IFNAMSIZ];
int rfdesc;
int wfdesc;
+ int ufdesc;
unsigned char *rbuf;
size_t rbuf_max;
size_t rbuf_offset;
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index 484953c..2e90cc8 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -135,6 +135,17 @@ assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
ip.ip_dst.s_addr = to;
ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
+
+ /*
+ * While the BPF -- used for broadcasts -- expects a "true" IP header
+ * with all the bytes in network byte order, the raw socket interface
+ * which is used for unicasts expects the ip_len field to be in host
+ * byte order. In both cases, the checksum has to be correct, so this
+ * is as good a place as any to turn the bytes around again.
+ */
+ if (to != INADDR_BROADCAST)
+ ip.ip_len = ntohs(ip.ip_len);
+
memcpy(&buf[*bufix], &ip, sizeof(ip));
*bufix += sizeof(ip);
OpenPOWER on IntegriCloud