This patch introduces an additional configuration flag, '--enable-use-lpf-socket', which allows using LPF for xmitting packets and BSD socket APIs to receive packets. In such mode, the packet socket used for transmission is not bound to the used device and the egress interface is specified on xmit via sendmsg. In the above scenario, the Linux kernel does not add an RX packet hook on the used device and this removes a lot of overhead for non-DHCP traffic processing compared to plain LPF mode. --- common/lpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- common/socket.c | 10 +++++++++- configure.ac | 17 +++++++++++++++++ includes/dhcpd.h | 4 ++++ includes/osdep.h | 7 +++++++ 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/common/lpf.c b/common/lpf.c index ee3820b..27943f0 100644 --- a/common/lpf.c +++ b/common/lpf.c @@ -75,10 +75,12 @@ int if_register_lpf (info) struct interface_info *info; { int sock; +#ifdef USE_LPF_RECEIVE union { struct sockaddr_ll ll; struct sockaddr common; } sa; +#endif struct ifreq ifr; /* Make an LPF socket. */ @@ -103,6 +105,8 @@ int if_register_lpf (info) if (ioctl (sock, SIOCGIFINDEX, &ifr)) log_fatal ("Failed to get interface index: %m"); + /* don't need to bind to send the packets */ +#ifdef USE_LPF_RECEIVE /* Bind to the interface name */ memset (&sa, 0, sizeof sa); sa.ll.sll_family = AF_PACKET; @@ -121,7 +125,9 @@ int if_register_lpf (info) log_fatal ("Bind socket to interface: %m"); } - +#else + info -> index = ifr.ifr_ifindex; +#endif get_hw_addr(info->name, &info->hw_address); return sock; @@ -324,6 +330,11 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct sockaddr_in *to; struct hardware *hto; { +#ifndef USE_LPF_RECEIVE + struct msghdr m; + struct sockaddr_ll ll; + struct iovec v; +#endif unsigned hbufp = 0, ibufp = 0; double hh [16]; double ih [1536 / sizeof (double)]; @@ -347,7 +358,34 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto) to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); memcpy (buf + ibufp, raw, len); + +#ifdef USE_LPF_RECEIVE result = write(interface->wfdesc, buf + fudge, ibufp + len - fudge); +#else + /* The packet socket is unbound, the egress interface must be + * specified via sendmsg */ + memset(&m, 0, sizeof(m)); + + /* Set the target interface */ + memset (&ll, 0, sizeof ll); + ll.sll_family = AF_PACKET; + ll.sll_protocol = SOCK_RAW; + ll.sll_ifindex = interface->index; + m.msg_name = ≪ + m.msg_namelen = sizeof(ll); + + /* + * Set the data buffer we're sending. (Using this wacky + * "scatter-gather" stuff... we only have a single chunk + * of data to send, so we declare a single vector entry.) + */ + v.iov_base = (char *) buf + fudge; + v.iov_len = ibufp + len - fudge; + m.msg_iov = &v; + m.msg_iovlen = 1; + + result = sendmsg(interface->wfdesc, &m, 0); +#endif if (result < 0) log_error ("send_packet: %m"); return result; @@ -469,25 +507,38 @@ ssize_t receive_packet (interface, buf, len, from, hfrom) memcpy(buf, &ibuf[bufix], paylen); return paylen; } +#endif +#if defined (USE_LPF_SEND) int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } +#endif +#if defined (USE_LPF_RECEIVE) int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } +#endif + +#if defined (USE_LPF_RECEIVE) || defined (USE_LPF_SOCKETS) int supports_multiple_interfaces (ip) struct interface_info *ip; { +#if defined (USE_LPF_SOCKETS) + if (!supports_multiple_interfaces_socket(ip)) + return 0; +#endif return 1; } +#endif +#if defined (USE_LPF_RECEIVE) || defined (USE_LPF_SOCKETS) void maybe_setup_fallback () { isc_result_t status; diff --git a/common/socket.c b/common/socket.c index 494528e..dafb634 100644 --- a/common/socket.c +++ b/common/socket.c @@ -55,6 +55,9 @@ # define if_reinitialize_send if_reinitialize_fallback # endif #endif +#ifdef USE_LPF_SOCKETS +# define supports_multiple_interfaces supports_multiple_interfaces_socket +#endif #if defined(DHCPv6) /* @@ -1101,7 +1104,9 @@ int can_unicast_without_arp (ip) { return 0; } +#endif +#if defined (USE_SOCKET_RECEIVE) int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { @@ -1111,7 +1116,9 @@ int can_receive_unicast_unconfigured (ip) return 0; #endif } +#endif +#if defined (USE_SOCKET_SEND) || defined (USE_LPF_SOCKETS) int supports_multiple_interfaces (ip) struct interface_info *ip; { @@ -1123,10 +1130,11 @@ int supports_multiple_interfaces (ip) return(0); #endif } +#endif /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, do not. */ - +#if defined (USE_SOCKET_SEND) void maybe_setup_fallback () { #if defined (USE_SOCKET_FALLBACK) diff --git a/configure.ac b/configure.ac index 3cabf64..9ea729a 100644 --- a/configure.ac +++ b/configure.ac @@ -203,6 +203,16 @@ if test "$enable_use_sockets" = "yes"; then [Define to 1 to use the standard BSD socket API.]) fi +AC_ARG_ENABLE(use_lpf_sockets, + AS_HELP_STRING([--enable-use-lpf-sockets],[use the standard BSD socket API for rx and LPF for send (default is no)])) + +if test "$enable_use_lpf_sockets" = "yes"; then + if test "$enable_use_sockets" = "yes"; then + AC_MSG_ERROR([please specify only one of --enable-use-lbf-sockets and --enable-use-sockets]) + fi +fi + + # Try to hnadle incorrect byte order for secs field # This is off by default AC_ARG_ENABLE(secs_byteorder, @@ -523,7 +533,14 @@ if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) + if test "$enable_use_lpf_sockets" = "yes"; then + AC_DEFINE([USE_LPF_SOCKETS], [1], + [Define to 1 to use the LP socket for sending packets and BSD ABI receiving them.]) + fi else + if test "$enable_use_lpf_socket" = "yes"; then + AC_MSG_ERROR([use_lpf_sockets only supported with LPF and LPF not present]) + fi AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" then diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 6b212be..ec00b29 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -2635,6 +2635,10 @@ int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif +#if defined (USE_LPF_SOCKETS) +int supports_multiple_interfaces_socket (struct interface_info *); +#endif + void if_register6(struct interface_info *info, int do_multicast); void if_register_linklocal6(struct interface_info *info); ssize_t receive_packet6(struct interface_info *interface, diff --git a/includes/osdep.h b/includes/osdep.h index cfae90b..5d6fb6a 100644 --- a/includes/osdep.h +++ b/includes/osdep.h @@ -55,6 +55,7 @@ #if !defined (USE_SOCKETS) && \ !defined (USE_SOCKET_SEND) && \ !defined (USE_SOCKET_RECEIVE) && \ + !defined (USE_LPF_SOCKETS) && \ !defined (USE_RAW_SOCKETS) && \ !defined (USE_RAW_SEND) && \ !defined (USE_SOCKET_RECEIVE) && \ @@ -109,6 +110,12 @@ # endif #endif +#ifdef USE_LPF_SOCKETS +# define USE_LPF_SEND +# define USE_SOCKET_RECEIVE +# define USE_LPF_HWADDR +#endif + #ifdef USE_RAW_SOCKETS # define USE_RAW_SEND # define USE_SOCKET_RECEIVE -- 1.8.3.1