I'm working on generic 802.11 stack for Linux. One problem I met so far with dhcp is dhclient stopped working after I use 802.11 header as the link layer header (as opposed to 802.3 header before). The root cause is dhclient uses PF_PACKET and SOCK_RAW to capture the whole link level packet. Since it doesn't understand 802.11 protocol, it fails to parse. One possible fix is to add the 802.11 header parsing code into dhcp package. But the problem is the 802.11 header might be too difficult/impossible to build considering about 802.11e (QoS), 802.11i (encryption), etc. Another way (which is better as I think) is to use the cooked packets supported (PF_PACKET and SOCK_DGRAM) by Linux. The kernel will add/remove the link level header after/before it gets/sends a packet from the userspace. So the user application actually doesn't need to worry about how to build the underlined hardware header. It is also known how pump(8) does. The patch adds this cooked packet feature support for dhcp-3.0. Signed-off-by: Zhu Yi -- diff -urp dhcp-3.0pl1/common/ethernet.c dhcp-3.0pl1-ieee80211/common/ethernet.c --- dhcp-3.0pl1/common/ethernet.c 2001-06-15 03:15:27.000000000 +0800 +++ dhcp-3.0pl1-ieee80211/common/ethernet.c 2005-08-11 17:03:01.000000000 +0800 @@ -55,6 +55,19 @@ static char copyright[] = #if defined (PACKET_ASSEMBLY) /* Assemble an hardware header... */ +int assemble_ethernet_dhost (interface, buf, to) + struct interface_info *interface; + unsigned char *buf; + struct hardware *to; +{ + if (to && to -> hlen == 7) /* XXX */ + memcpy (buf, &to -> hbuf [1], ETHER_ADDR_LEN); + else + memset (buf, 0xff, ETHER_ADDR_LEN); + + return ETHER_ADDR_LEN; +} + void assemble_ethernet_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; @@ -63,11 +76,7 @@ void assemble_ethernet_header (interface { struct isc_ether_header eh; - if (to && to -> hlen == 7) /* XXX */ - memcpy (eh.ether_dhost, &to -> hbuf [1], - sizeof eh.ether_dhost); - else - memset (eh.ether_dhost, 0xff, sizeof (eh.ether_dhost)); + assemble_ethernet_dhost(interface, eh.ether_dhost, to); if (interface -> hw_address.hlen - 1 == sizeof (eh.ether_shost)) memcpy (eh.ether_shost, &interface -> hw_address.hbuf [1], sizeof (eh.ether_shost)); diff -urp dhcp-3.0pl1/common/fddi.c dhcp-3.0pl1-ieee80211/common/fddi.c --- dhcp-3.0pl1/common/fddi.c 2000-04-19 07:02:09.000000000 +0800 +++ dhcp-3.0pl1-ieee80211/common/fddi.c 2005-08-11 17:07:35.000000000 +0800 @@ -59,6 +59,17 @@ static char copyright[] = #if defined (PACKET_ASSEMBLY) /* Assemble an hardware header... */ +int assemble_fddi_dhost (interface, buf, to) + struct interface_info *interface; + unsigned char *buf; + struct hardware *to; +{ + if (to && to -> hlen == 7) + memcpy (buf, &to -> hbuf [1], FDDI_K_ALEN); + + return FDDI_K_ALEN; +} + void assemble_fddi_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; @@ -68,9 +79,7 @@ void assemble_fddi_header (interface, bu struct fddi_header fh; struct llc lh; - if (to && to -> hlen == 7) - memcpy (fh.fddi_dhost, &to -> hbuf [1], - sizeof (fh.fddi_dhost)); + assemble_fddi_dhost(interface, fh.fddi_dhost, to); memcpy (fh.fddi_shost, &interface -> hw_address.hbuf [1], sizeof (fh.fddi_shost)); fh.fddi_fc = FDDIFC_LLC_ASYNC; diff -urp dhcp-3.0pl1/common/lpf.c dhcp-3.0pl1-ieee80211/common/lpf.c --- dhcp-3.0pl1/common/lpf.c 2001-04-24 08:36:00.000000000 +0800 +++ dhcp-3.0pl1-ieee80211/common/lpf.c 2005-08-11 17:14:26.000000000 +0800 @@ -49,6 +49,7 @@ static char copyright[] = #include #include #include +#include #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" @@ -80,10 +81,10 @@ int if_register_lpf (info) int sock; char filename[50]; int b; - struct sockaddr sa; + struct sockaddr_ll ll; /* Make an LPF socket. */ - if ((sock = socket(PF_PACKET, SOCK_PACKET, + if ((sock = socket(PF_PACKET, SOCK_DGRAM, htons((short)ETH_P_ALL))) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || @@ -98,15 +99,21 @@ int if_register_lpf (info) log_fatal ("Open a socket for LPF: %m"); } + if (ioctl(sock, SIOCGIFINDEX, info->ifp) < 0) { + log_fatal ("ioctl[SIOCGIFINDEX] to interface: %m"); + close(sock); + return -1; + } + /* Bind to the interface name */ - memset (&sa, 0, sizeof sa); - sa.sa_family = AF_PACKET; - strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data); - if (bind (sock, &sa, sizeof sa)) { + memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = info->ifp->ifr_ifindex; + if (bind(sock, (struct sockaddr *)&ll, sizeof(ll)) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) { - log_error ("socket: %m - make sure"); + log_error ("bind: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", @@ -226,22 +233,30 @@ static void lpf_gen_filter_setup (info) { struct sock_fprog p; - /* Set up the bpf filter program structure. This is defined in - bpf.c */ - p.len = dhcp_bpf_filter_len; - p.filter = dhcp_bpf_filter; + /* Patch the filter program for Linux cooked packets + * with the link level header removed. */ + dhcp_bpf_filter [1].jf = 0; /* bypass the IP packet type check */ + dhcp_bpf_filter [2].k -= ETH_HLEN; + dhcp_bpf_filter [4].k -= ETH_HLEN; + dhcp_bpf_filter [6].k -= ETH_HLEN; + dhcp_bpf_filter [7].k -= ETH_HLEN; /* Patch the server port into the LPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ dhcp_bpf_filter [8].k = ntohs ((short)local_port); + /* Set up the bpf filter program structure. This is defined in + bpf.c */ + p.len = dhcp_bpf_filter_len; + p.filter = dhcp_bpf_filter; + if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof p) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT) { - log_error ("socket: %m - make sure"); + log_error ("setsockopt: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", @@ -275,7 +290,7 @@ static void lpf_tr_filter_setup (info) if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT) { - log_error ("socket: %m - make sure"); + log_error ("setsockopt: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", @@ -298,23 +313,18 @@ ssize_t send_packet (interface, packet, struct sockaddr_in *to; struct hardware *hto; { - unsigned hbufp = 0, ibufp = 0; + unsigned ibufp = 0; double hh [16]; double ih [1536 / sizeof (double)]; unsigned char *buf = (unsigned char *)ih; - struct sockaddr sa; + struct sockaddr_ll ll; int result; - int fudge; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); /* Assemble the headers... */ - assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto); - fudge = hbufp % 4; /* IP header must be word-aligned. */ - memcpy (buf + fudge, (unsigned char *)hh, hbufp); - ibufp = hbufp + fudge; assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); @@ -322,13 +332,14 @@ ssize_t send_packet (interface, packet, /* For some reason, SOCK_PACKET sockets can't be connected, so we have to do a sentdo every time. */ - memset (&sa, 0, sizeof sa); - sa.sa_family = AF_PACKET; - strncpy (sa.sa_data, - (const char *)interface -> ifp, sizeof sa.sa_data); + memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = interface->ifp->ifr_ifindex; + ll.sll_protocol = htons(ETH_P_IP); + ll.sll_halen = assemble_hw_dhost(interface, ll.sll_addr, to); - result = sendto (interface -> wfdesc, - buf + fudge, ibufp + len - fudge, 0, &sa, sizeof sa); + result = sendto (interface -> wfdesc, buf, ibufp + len, 0, + (struct sockaddr *)&ll, sizeof(ll)); if (result < 0) log_error ("send_packet: %m"); return result; @@ -353,20 +364,6 @@ ssize_t receive_packet (interface, buf, if (length <= 0) return length; - bufix = 0; - /* Decode the physical header... */ - offset = decode_hw_header (interface, ibuf, bufix, hfrom); - - /* If a physical layer checksum failed (dunno of any - physical layer that supports this, but WTH), skip this - packet. */ - if (offset < 0) { - return 0; - } - - bufix += offset; - length -= offset; - /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header (interface, ibuf, bufix, from, (unsigned char *)0, (unsigned)length); diff -urp dhcp-3.0pl1/common/packet.c dhcp-3.0pl1-ieee80211/common/packet.c --- dhcp-3.0pl1/common/packet.c 2001-06-01 03:28:51.000000000 +0800 +++ dhcp-3.0pl1-ieee80211/common/packet.c 2005-08-11 17:12:45.000000000 +0800 @@ -113,6 +113,24 @@ u_int32_t wrapsum (sum) } #ifdef PACKET_ASSEMBLY +int assemble_hw_dhost (interface, buf, to) + struct interface_info *interface; + unsigned char *buf; + struct hardware *to; +{ +#if defined (HAVE_TR_SUPPORT) + if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802) + return assemble_tr_dhost (interface, buf, to); + else +#endif +#if defined (DEC_FDDI) + if (interface -> hw_address.hbuf [0] == HTYPE_FDDI) + return assemble_fddi_dhost (interface, buf, to); + else +#endif + return assemble_ethernet_dhost (interface, buf, to); +} + void assemble_hw_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; diff -urp dhcp-3.0pl1/common/tr.c dhcp-3.0pl1-ieee80211/common/tr.c --- dhcp-3.0pl1/common/tr.c 2001-04-28 06:23:02.000000000 +0800 +++ dhcp-3.0pl1-ieee80211/common/tr.c 2005-08-11 17:11:13.000000000 +0800 @@ -78,6 +78,19 @@ static struct routing_entry *routing_inf static int routing_timeout = 10; static struct timeval routing_timer; +int assemble_tr_dhost (interface, buf, to) + struct interface_info *interface; + unsigned char *buf; + struct hardware *to; +{ + if (to && to -> hlen == 7) /* XXX */ + memcpy (buf, &to -> hbuf [1], TR_ALEN); + else + memset (buf, 0xff, TR_ALEN); + + return TR_ALEN; +} + void assemble_tr_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; @@ -97,10 +110,7 @@ void assemble_tr_header (interface, buf, else memset (trh->saddr, 0x00, sizeof (trh->saddr)); - if (to && to -> hlen == 7) /* XXX */ - memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr); - else - memset (trh->daddr, 0xff, sizeof (trh->daddr)); + assemble_tr_dhost(interface, trh->daddr, to); hdr_len = insert_source_routing (trh, interface);