diff options
Diffstat (limited to 'sbin/dhclient')
-rw-r--r-- | sbin/dhclient/bpf.c | 45 | ||||
-rw-r--r-- | sbin/dhclient/dhcpd.h | 3 | ||||
-rw-r--r-- | sbin/dhclient/packet.c | 11 |
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); |