We have a bunch of really old PXE clients that ignore IP packets with the DF bit set, and we cannot (due to security updates on the system) change the ip_pmtu_strategy in a way that causes hp-ux to not set the DF bit when sending packets over normal sockets. Thus I forward-ported the DLPI stuff to have more control. diff -urp dhcp-3.0.1/common/discover.c dhcp-3.0.1-new/common/discover.c --- dhcp-3.0.1/common/discover.c 2004-06-10 19:59:16.000000000 +0200 +++ dhcp-3.0.1-new/common/discover.c 2004-08-30 15:25:49.000000000 +0200 @@ -678,9 +678,10 @@ void discover_interfaces (state) #if defined (HAVE_SETFD) if (fallback_interface) { - if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) - log_error ("Can't set close-on-exec on fallback: %m"); - if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { + if ( fallback_interface -> rfdesc != -1) + if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on fallback: %m"); + if ( (fallback_interface -> wfdesc != -1) && (fallback_interface -> rfdesc != fallback_interface -> wfdesc) ) { if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) log_error ("Can't set close-on-exec on fallback: %m"); } diff -urp dhcp-3.0.1/common/dlpi.c dhcp-3.0.1-new/common/dlpi.c --- dhcp-3.0.1/common/dlpi.c 2004-06-10 19:59:17.000000000 +0200 +++ dhcp-3.0.1-new/common/dlpi.c 2004-08-30 15:31:47.000000000 +0200 @@ -77,6 +77,24 @@ * to sleep. */ +/* + * Notes about HP-UX + * + * Most of the HP-DLPI code is taken from http://www.j-mulders.demon.nl/unix/hp-related/dlpi.c + * which implements the use of the HP-UX DLPI for dhcpd 2.0 (I don't know precisely + * which version, it is not the latest). + * + * Some more code was taken from revision 1.107 of pcap-dlpi.c + * from the libpcap project, which is licensed under a BSD license. + * See http://cvs.tcpdump.org/cgi-bin/cvsweb/libpcap/pcap-dlpi.c?rev=1.107 + * for the exact sources. + * Specifically, split_dname, send_dlpi_request and get_dlpi_ppa were taken + * from there and adapted to dhcpd's error handling etc. + * + * August 2004, Johannes Berg + */ + + #ifndef lint static char copyright[] = "$Id: dlpi.c,v 1.28.2.2 2004/06/10 17:59:17 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; @@ -102,6 +120,11 @@ static char copyright[] = # include "includes/netinet/udp.h" # include "includes/netinet/if_ether.h" +#ifdef HP_DLPI +# include +# define HP_DLPI_DEVICE "/dev/dlpi" +#endif + # ifdef USE_DLPI_PFMOD # ifdef USE_DLPI_RAW # define DLPI_MODNAME "DLPI+RAW+PFMOD" @@ -112,7 +135,11 @@ static char copyright[] = # ifdef USE_DLPI_RAW # define DLPI_MODNAME "DLPI+RAW" # else -# define DLPI_MODNAME "DLPI" +# ifdef HP_DLPI +# define DLPI_MODNAME "HP-DLPI" +# else +# define DLPI_MODNAME "DLPI" +# endif # endif # endif @@ -120,6 +147,10 @@ static char copyright[] = # define ABS(x) ((x) >= 0 ? (x) : 0-(x)) # endif +#if defined(HP_DLPI) && defined(USE_DLPI_RECEIVE) +# error "HP_DLPI and USE_DLPI_RECEIVE are incompatible" +#endif + static int strioctl PROTO ((int fd, int cmd, int timeout, int len, char *dp)); #define DLPI_MAXDLBUF 8192 /* Buffer size */ @@ -127,7 +158,10 @@ static int strioctl PROTO ((int fd, int #define DLPI_DEVDIR "/dev/" /* Device directory */ static int dlpiopen PROTO ((char *ifname)); -static int dlpiunit PROTO ((char *ifname)); +#ifdef HP_DLPI +static char * split_dname PROTO ((char *device, int *unitp)); +#endif +static int dlpiunit PROTO ((int fd, char *ifname)); static int dlpiinforeq PROTO ((int fd)); static int dlpiphysaddrreq PROTO ((int fd, unsigned long addrtype)); static int dlpiattachreq PROTO ((int fd, unsigned long ppa)); @@ -241,7 +275,7 @@ int if_register_dlpi (info) * Attach to the device. If this fails, the device * does not exist. */ - unit = dlpiunit (info -> name); + unit = dlpiunit (sock, info -> name); if (dlpiattachreq (sock, unit) < 0 || dlpiokack (sock, (char *)buf) < 0) { @@ -252,7 +286,31 @@ int if_register_dlpi (info) /* * Bind to the IP service access point (SAP), connectionless (CLDLS). */ + +#ifdef HP_DLPI + /* + * AAARRRGGGGGGH !!!!!! + * + * We want to bind (talk here, and listen for) .. ETHERTYPE_IP (0x800). + * This does not work. It responds with DL_BADARR (0x04). + * + * The manual: Valid ethernet types range from 0x600 to 0xFFFF, + * excluding reserved ethertypes. + * + * Sjjjeeeeezzzz ! + * + * Bind here to an unexisting SAP and use raw dlpi for + * sending packets. + * This means we cannot use dlpi's filtering capabilities for RECEIVING + * ethertype_ip packets. This will mean even more packets to appear in + * the userland filter. DLPI_RECEIVE without a packetfilter is a bad + * thing and therefore not done. + * + */ + if (dlpibindreq (sock, ETHERTYPE_IP+1, 0, DL_HP_RAWDLS, 0, 0) < 0 +#else if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0 +#endif || dlpibindack (sock, (char *)buf) < 0) { log_fatal ("Can't bind DLPI device for %s: %m", info -> name); } @@ -347,7 +405,8 @@ void if_register_send (info) #endif if (!quiet_interface_discovery) - log_info ("Sending on DLPI/%s/%s%s%s", + log_info ("Sending on %s/%s/%s%s%s", + DLPI_MODNAME, info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, @@ -377,7 +436,8 @@ void if_deregister_send (info) info -> wfdesc = -1; if (!quiet_interface_discovery) - log_info ("Disabling output on DLPI/%s/%s%s%s", + log_info ("Disabling output on %s/%s/%s%s%s", + DLPI_MODNAME, info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, @@ -462,7 +522,8 @@ void if_register_receive (info) #endif /* USE_DLPI_PFMOD */ if (!quiet_interface_discovery) - log_info ("Listening on DLPI/%s/%s%s%s", + log_info ("Listening on %s/%s/%s%s%s", + DLPI_MODNAME, info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, @@ -492,7 +553,8 @@ void if_deregister_receive (info) info -> rfdesc = -1; if (!quiet_interface_discovery) - log_info ("Disabling input on DLPI/%s/%s%s%s", + log_info ("Disabling input on %s/%s/%s%s%s", + DLPI_MODNAME, info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, @@ -513,6 +575,52 @@ ssize_t send_packet (interface, packet, struct sockaddr_in *to; struct hardware *hto; { +#ifdef HP_DLPI + int result; + struct strbuf dlpi_ctrl; + struct strbuf dlpi_data; + u_long dlpi_ctrl_area [1500]; + u_long dlpi_data_area [1500]; + dl_hp_rawdata_req_t *rawdat_req; + /* + * check if we need to fallback + */ + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + /* + * send the packet using DLPI + */ + dlpi_ctrl.maxlen = sizeof (dlpi_ctrl_area); + dlpi_ctrl.buf = (char *) dlpi_ctrl_area; + dlpi_ctrl.len = 0; + + dlpi_data.maxlen = sizeof (dlpi_data_area); + dlpi_data.buf = (char *) dlpi_data_area; + dlpi_data.len = 0; + + assemble_hw_header (interface, (unsigned char*)dlpi_data.buf, (unsigned*)&dlpi_data.len, hto); + assemble_udp_ip_header (interface, (unsigned char*)dlpi_data.buf, + (unsigned*)&dlpi_data.len, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + + /* + * append the data for this packet + */ + memcpy ((u_char *) dlpi_data.buf + dlpi_data.len, raw, len); + dlpi_data.len += len; + + rawdat_req = (dl_hp_rawdata_req_t *) dlpi_ctrl.buf; + rawdat_req -> dl_primitive = DL_HP_RAWDATA_REQ; + dlpi_ctrl.len = sizeof (dl_unitdata_req_t); + dlpi_ctrl.maxlen = 0; + + if ((result = putmsg (interface -> wfdesc, &dlpi_ctrl, &dlpi_data, 0)) < 0) + log_error ("send_packet: %m"); + return result; + +#else unsigned hbufp = 0; double hh [32]; double ih [1536 / sizeof (double)]; @@ -600,6 +708,7 @@ ssize_t send_packet (interface, packet, if (result < 0) log_error ("send_packet: %m"); return result; +#endif /* HP_DLPI */ } #endif /* USE_DLPI_SEND */ @@ -715,15 +824,292 @@ ssize_t receive_packet (interface, buf, #define DLPI_MAXWAIT 15 /* Max timeout */ +#ifdef HP_DLPI +/* + * The code in this section is from libpcap + */ + + +/* + * Split a device name into a device type name and a unit number; + * return the a pointer to the beginning of the unit number, which + * is the end of the device type name, and set "*unitp" to the unit + * number. + */ +static char * +split_dname(char *device, int *unitp) +{ + char *cp; + char *eos; + long unit; + + /* + * Look for a number at the end of the device name string. + */ + cp = device + strlen(device) - 1; + if (*cp < '0' || *cp > '9') { + log_fatal("%s missing unit number", + device); + return (NULL); + } + + /* Digits at end of string are unit number */ + while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9') + cp--; + + errno = 0; + unit = strtol(cp, &eos, 10); + if (*eos != '\0') { + log_fatal("%s bad unit number", device); + return (NULL); + } + if (errno == ERANGE || unit > INT_MAX) { + log_fatal("%s unit number too large", + device); + return (NULL); + } + if (unit < 0) { + log_fatal("%s unit number is negative", + device); + return (NULL); + } + *unitp = (int)unit; + return (cp); +} + +static int +send_dlpi_request(fd, ptr, len, what) + int fd; + char * ptr; + int len; + char * what; +{ + struct strbuf ctl; + int flags; + + ctl.maxlen = 0; + ctl.len = len; + ctl.buf = ptr; + + flags = 0; + if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) { + log_fatal ("send_dlpi_request: putmsg error"); + return (-1); + } + return (0); +} + /* - * Parse an interface name and extract the unit number + * Determine ppa number that specifies ifname. + * + * If the "dl_hp_ppa_info_t" doesn't have a "dl_module_id_1" member, + * the code that's used here is the old code for HP-UX 10.x. + * + * However, HP-UX 10.20, at least, appears to have such a member + * in its "dl_hp_ppa_info_t" structure, so the new code is used. + * The new code didn't work on an old 10.20 system on which Rick + * Jones of HP tried it, but with later patches installed, it + * worked - it appears that the older system had those members but + * didn't put anything in them, so, if the search by name fails, we + * do the old search. + * + * Rick suggests that making sure your system is "up on the latest + * lancommon/DLPI/driver patches" is probably a good idea; it'd fix + * that problem, as well as allowing libpcap to see packets sent + * from the system on which the libpcap application is being run. + * (On 10.20, in addition to getting the latest patches, you need + * to turn the kernel "lanc_outbound_promisc_flag" flag on with ADB; + * a posting to "comp.sys.hp.hpux" at + * + * http://www.deja.com/[ST_rn=ps]/getdoc.xp?AN=558092266 + * + * says that, to see the machine's outgoing traffic, you'd need to + * apply the right patches to your system, and also set that variable + * with: + +echo 'lanc_outbound_promisc_flag/W1' | /usr/bin/adb -w /stand/vmunix /dev/kmem + + * which could be put in, for example, "/sbin/init.d/lan". + * + * Setting the variable is not necessary on HP-UX 11.x. + */ +static int +get_dlpi_ppa(register int fd, register const char *device, register int unit) +{ + register dl_hp_ppa_ack_t *ap; + register dl_hp_ppa_info_t *ipstart, *ip; + register int i; + char dname[100]; + register u_long majdev; + struct stat statbuf; + dl_hp_ppa_req_t req; + char buf[DLPI_MAXDLBUF]; + char *ppa_data_buf; + dl_hp_ppa_ack_t *dlp; + struct strbuf ctl; + int flags; + int ppa; + + memset((char *)&req, 0, sizeof(req)); + req.dl_primitive = DL_HP_PPA_REQ; + + memset((char *)buf, 0, sizeof(buf)); + if (send_dlpi_request(fd, (char *)&req, sizeof(req), "hpppa") < 0) + return (-1); + + ctl.maxlen = DL_HP_PPA_ACK_SIZE; + ctl.len = 0; + ctl.buf = (char *)buf; + + flags = 0; + /* + * DLPI may return a big chunk of data for a DL_HP_PPA_REQ. The normal + * recv_ack will fail because it set the maxlen to MAXDLBUF (8192) + * which is NOT big enough for a DL_HP_PPA_REQ. + * + * This causes libpcap applications to fail on a system with HP-APA + * installed. + * + * To figure out how big the returned data is, we first call getmsg + * to get the small head and peek at the head to get the actual data + * length, and then issue another getmsg to get the actual PPA data. + */ + /* get the head first */ + if (getmsg(fd, &ctl, (struct strbuf *)NULL, &flags) < 0) { + log_fatal ("get_dlpi_ppa: hpppa getmsg error"); + return (-1); + } + + dlp = (dl_hp_ppa_ack_t *)ctl.buf; + if (dlp->dl_primitive != DL_HP_PPA_ACK) { + log_fatal("get_dlpi_ppa: hpppa unexpected primitive ack0x%x", + (int)dlp->dl_primitive); + return (-1); + } + + if (ctl.len < DL_HP_PPA_ACK_SIZE) { + log_fatal ("get_dlpi_ppa: hpppa ack too small (%d < %lu)", + ctl.len, (unsigned long)DL_HP_PPA_ACK_SIZE); + return (-1); + } + + /* allocate buffer */ + if ((ppa_data_buf = (char *)malloc(dlp->dl_length)) == NULL) { + log_fatal("get_dlpi_ppa: hpppa malloc error number %d", errno); + return (-1); + } + ctl.maxlen = dlp->dl_length; + ctl.len = 0; + ctl.buf = (char *)ppa_data_buf; + /* get the data */ + if (getmsg(fd, &ctl, (struct strbuf *)NULL, &flags) < 0) { + log_fatal("get_dlpi_ppa: hpppa getmsg error number %d", errno); + free(ppa_data_buf); + return (-1); + } + if (ctl.len < dlp->dl_length) { + log_fatal ("get_dlpi_ppa: hpppa ack too small (%d < %d)", + ctl.len, dlp->dl_length); + free(ppa_data_buf); + return (-1); + } + + ap = (dl_hp_ppa_ack_t *)buf; + ipstart = (dl_hp_ppa_info_t *)ppa_data_buf; + ip = ipstart; + + /* + * The "dl_hp_ppa_info_t" structure has a "dl_module_id_1" + * member that should, in theory, contain the part of the + * name for the device that comes before the unit number, + * and should also have a "dl_module_id_2" member that may + * contain an alternate name (e.g., I think Ethernet devices + * have both "lan", for "lanN", and "snap", for "snapN", with + * the former being for Ethernet packets and the latter being + * for 802.3/802.2 packets). + * + * Search for the device that has the specified name and + * instance number. + */ + for (i = 0; i < ap->dl_count; i++) { + if ((strcmp((const char *)ip->dl_module_id_1, device) == 0 || + strcmp((const char *)ip->dl_module_id_2, device) == 0) && + ip->dl_instance_num == unit) + break; + + ip = (dl_hp_ppa_info_t *)((u_char *)ipstart + ip->dl_next_offset); + } + + if (i == ap->dl_count) { + /* + * Well, we didn't, or can't, find the device by name. + * + * HP-UX 10.20, whilst it has "dl_module_id_1" and + * "dl_module_id_2" fields in the "dl_hp_ppa_info_t", + * doesn't seem to fill them in unless the system is + * at a reasonably up-to-date patch level. + * + * Older HP-UX 10.x systems might not have those fields + * at all. + * + * Therefore, we'll search for the entry with the major + * device number of a device with the name "/dev/", + * if such a device exists, as the old code did. + */ + snprintf(dname, sizeof(dname), "/dev/%s%d", device, unit); + if (stat(dname, &statbuf) < 0) { + log_fatal ("stat: %s: %d", + dname, errno); + return (-1); + } + majdev = major(statbuf.st_rdev); + + ip = ipstart; + + for (i = 0; i < ap->dl_count; i++) { + if (ip->dl_mjr_num == majdev && + ip->dl_instance_num == unit) + break; + + ip = (dl_hp_ppa_info_t *)((u_char *)ipstart + ip->dl_next_offset); + } + } + if (i == ap->dl_count) { + log_fatal("can't find /dev/dlpi PPA for %s%d", device, unit); + return (-1); + } + if (ip->dl_hdw_state == HDW_DEAD) { + log_fatal("%s%d: hardware state: DOWN\n", device, unit); + free(ppa_data_buf); + return (-1); + } + ppa = ip->dl_ppa; + free(ppa_data_buf); + return (ppa); +} +#endif + +/* + * Parse an interface name and extract the unit number. + * This does not necessarily work on HP-UX always, so use + * different code there. */ -static int dlpiunit (ifname) +static int dlpiunit (fd, ifname) + int fd; /* only used for HP-UX */ char *ifname; { - int fd; +#ifdef HP_DLPI + char * res; + int unitnumber; + char tmpbuf[100]; + tmpbuf[99]=0; + strncpy(tmpbuf, ifname, 99); + res = split_dname(tmpbuf, &unitnumber); + *res = 0; /* 0 terminate device type name */ + return get_dlpi_ppa (fd, tmpbuf, unitnumber); +#else char *cp, *dp, *ep; int unit; @@ -745,6 +1131,7 @@ static int dlpiunit (ifname) } return unit; +#endif } /* @@ -753,6 +1140,9 @@ static int dlpiunit (ifname) static int dlpiopen (ifname) char *ifname; { +#ifdef HP_DLPI + return open(HP_DLPI_DEVICE, O_RDWR); +#else char devname [50]; char *cp, *dp, *ep; @@ -784,6 +1174,7 @@ static int dlpiopen (ifname) *dp = '\0'; return open (devname, O_RDWR, 0); +#endif } /* @@ -1034,7 +1425,7 @@ static int dlpiinfoack (fd, bufp) /* * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq. */ -int dlpiphysaddrack (fd, bufp) +static int dlpiphysaddrack (fd, bufp) char *bufp; int fd; { @@ -1065,7 +1456,7 @@ int dlpiphysaddrack (fd, bufp) return 0; } -int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen) +static int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen) int fd; unsigned char *addr; int addrlen; @@ -1080,7 +1471,11 @@ int dlpiunitdatareq (fd, addr, addrlen, /* Set up the control information... */ dlp = (union DL_primitives *)buf; + #ifdef HP_DLPI + dlp -> unitdata_req.dl_primitive = DL_HP_RAWDATA_REQ; + #else dlp -> unitdata_req.dl_primitive = DL_UNITDATA_REQ; + #endif dlp -> unitdata_req.dl_dest_addr_length = addrlen; dlp -> unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); dlp -> unitdata_req.dl_priority.dl_min = minpri; diff -urp dhcp-3.0.1/common/socket.c dhcp-3.0.1-new/common/socket.c --- dhcp-3.0.1/common/socket.c 2004-06-10 19:59:21.000000000 +0200 +++ dhcp-3.0.1-new/common/socket.c 2004-08-30 15:24:17.000000000 +0200 @@ -153,6 +153,8 @@ int if_register_socket (info) } #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ +static int read_file_descriptor; + #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) void if_register_send (info) struct interface_info *info; @@ -165,7 +167,7 @@ void if_register_send (info) info -> rfdesc = info -> wfdesc; #endif #else - info -> wfdesc = info -> rfdesc; + info -> wfdesc = read_file_descriptor; #endif if (!quiet_interface_discovery) log_info ("Sending on Socket/%s%s%s", @@ -200,7 +202,7 @@ void if_register_receive (info) { /* If we're using the socket API for sending and receiving, we don't need to register this interface twice. */ - info -> rfdesc = if_register_socket (info); + read_file_descriptor = info -> rfdesc = if_register_socket (info); if (!quiet_interface_discovery) log_info ("Listening on Socket/%s%s%s", info -> name, diff -urp dhcp-3.0.1/includes/cf/hpux.h dhcp-3.0.1-new/includes/cf/hpux.h --- dhcp-3.0.1/includes/cf/hpux.h 2004-06-14 20:50:06.000000000 +0200 +++ dhcp-3.0.1-new/includes/cf/hpux.h 2004-08-30 15:24:17.000000000 +0200 @@ -81,7 +81,9 @@ extern int h_errno; #define VA_start(list, last) va_start (list) #endif -#define USE_SOCKETS 1 +#define USE_SOCKET_RECEIVE 1 +#define USE_DLPI_SEND 1 +#define HP_DLPI 1 #define EOL '\n' #define VOIDPTR void *