Author: philip
Date: Wed Oct 21 23:50:35 2009
New Revision: 198352
URL: http://svn.freebsd.org/changeset/base/198352

Log:
  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.
  
  Reviewed by:  brooks
  Obtained from:        OpenBSD (partly - I'm not convinced their solution can 
work)
  MFC after:    1 week (pending re approval)

Modified:
  head/sbin/dhclient/bpf.c
  head/sbin/dhclient/dhcpd.h
  head/sbin/dhclient/packet.c

Modified: head/sbin/dhclient/bpf.c
==============================================================================
--- head/sbin/dhclient/bpf.c    Wed Oct 21 20:59:12 2009        (r198351)
+++ head/sbin/dhclient/bpf.c    Wed Oct 21 23:50:35 2009        (r198352)
@@ -90,11 +90,23 @@ if_register_bpf(struct interface_info *i
 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 *inter
 {
        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);

Modified: head/sbin/dhclient/dhcpd.h
==============================================================================
--- head/sbin/dhclient/dhcpd.h  Wed Oct 21 20:59:12 2009        (r198351)
+++ head/sbin/dhclient/dhcpd.h  Wed Oct 21 23:50:35 2009        (r198352)
@@ -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;

Modified: head/sbin/dhclient/packet.c
==============================================================================
--- head/sbin/dhclient/packet.c Wed Oct 21 20:59:12 2009        (r198351)
+++ head/sbin/dhclient/packet.c Wed Oct 21 23:50:35 2009        (r198352)
@@ -135,6 +135,17 @@ assemble_udp_ip_header(unsigned char *bu
        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);
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to