summaryrefslogtreecommitdiff
path: root/sbin/dhclient
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/dhclient')
-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 8a669e1befb3..9f8e45fbfd21 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 8097f14b1de1..bd4c9c0b5e15 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 484953ca2d98..2e90cc85a8de 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);