/* socket.c BSD socket interface code... */ /* * Copyright (c) 1995, 1996, 1997, 1998, 1999 * The Internet Software Consortium. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This software has been written for the Internet Software Consortium * by Ted Lemon in cooperation with Vixie * 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''. */ /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). * This sockopt allows a socket to be bound to a particular interface, * thus enabling the use of DHCPD on a multihomed host. * If SO_BINDTODEVICE is defined in your system header files, the use of * this sockopt will be automatically enabled. * I have implemented it under Linux; other systems should be doable also. */ #ifndef lint static char copyright[] = "$Id: socket.c,v 1.1.1.8 1999/03/29 23:00:53 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" #ifdef USE_SOCKET_FALLBACK # define USE_SOCKET_SEND # define if_register_send if_register_fallback # define send_packet send_fallback # define if_reinitialize_send if_reinitialize_fallback #endif static int once = 0; /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_SOCKET_SEND void if_reinitialize_send (info) struct interface_info *info; { #if 0 #ifndef USE_SOCKET_RECEIVE once = 0; close (info -> wfdesc); #endif if_register_send (info); #endif } #endif #ifdef USE_SOCKET_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { #if 0 once = 0; close (info -> rfdesc); if_register_receive (info); #endif } #endif #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) /* Generic interface registration routine... */ int if_register_socket (info) struct interface_info *info; { struct sockaddr_in name; int sock; int flag; #if !defined (HAVE_SO_BINDTODEVICE) && !defined (USE_FALLBACK) /* Make sure only one interface is registered. */ if (once) error ("The standard socket API can only support %s", "hosts with a single network interface."); once = 1; #endif /* Set up the address we're going to bind to. */ name.sin_family = AF_INET; name.sin_port = local_port; name.sin_addr.s_addr = INADDR_ANY; memset (name.sin_zero, 0, sizeof (name.sin_zero)); /* Make a socket... */ if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) error ("Can't create dhcp socket: %m"); /* Set the REUSEADDR option so that we don't fail to start if we're being restarted. */ flag = 1; if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof flag) < 0) error ("Can't set SO_REUSEADDR option on dhcp socket: %m"); /* Set the BROADCAST option so that we can broadcast DHCP responses. */ if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *)&flag, sizeof flag) < 0) error ("Can't set SO_BROADCAST option on dhcp socket: %m"); /* Bind the socket to this interface's IP address. */ if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) error ("Can't bind to dhcp address: %m"); #if defined (HAVE_SO_BINDTODEVICE) /* Bind this socket to this interface. */ if (info -> ifp && setsockopt (sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)(info -> ifp), sizeof *(info -> ifp)) < 0) { error("setsockopt: SO_BINDTODEVICE: %m"); } #endif return sock; } #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE */ #ifdef USE_SOCKET_SEND void if_register_send (info) struct interface_info *info; { #ifndef USE_SOCKET_RECEIVE info -> wfdesc = if_register_socket (info); #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) note ("Sending on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_SOCKET_SEND */ #ifdef USE_SOCKET_RECEIVE void if_register_receive (info) struct interface_info *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); if (!quiet_interface_discovery) note ("Listening on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_SOCKET_RECEIVE */ #ifdef USE_SOCKET_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { int result; #ifdef IGNORE_HOSTUNREACH int retry = 0; do { #endif result = sendto (interface -> wfdesc, (char *)raw, len, 0, (struct sockaddr *)to, sizeof *to); #ifdef IGNORE_HOSTUNREACH } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && result < 0 && (errno == EHOSTUNREACH || errno == ECONNREFUSED) && retry++ < 10); #endif if (result < 0) { warn ("send_packet: %m"); if (errno == ENETUNREACH) warn ("send_packet: please consult README file %s", "regarding broadcast address."); } return result; } #endif /* USE_SOCKET_SEND */ #ifdef USE_SOCKET_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { int flen = sizeof *from; int result; #ifdef IGNORE_HOSTUNREACH int retry = 0; do { #endif result = recvfrom (interface -> rfdesc, (char *)buf, len, 0, (struct sockaddr *)from, &flen); #ifdef IGNORE_HOSTUNREACH } while (result < 0 && (errno == EHOSTUNREACH || errno == ECONNREFUSED) && retry++ < 10); #endif return result; } #endif /* USE_SOCKET_RECEIVE */ #ifdef USE_SOCKET_SEND /* This just reads in a packet and silently discards it. */ void fallback_discard (protocol) struct protocol *protocol; { char buf [1540]; struct sockaddr_in from; int flen = sizeof from; int status; struct interface_info *interface = protocol -> local; status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, (struct sockaddr *)&from, &flen); if (status < 0) warn ("fallback_discard: %m"); } #endif /* USE_SOCKET_SEND */ #if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) int can_unicast_without_arp () { return 0; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) return 1; #else return 0; #endif } /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, do not. */ void maybe_setup_fallback () { #if defined (SO_BINDTODEVICE) struct interface_info *fbi; fbi = setup_fallback (); if (fbi) { fbi -> wfdesc = if_register_socket (fbi); add_protocol ("fallback", fbi -> wfdesc, fallback_discard, fbi); } #endif } #endif /* USE_SOCKET_SEND && !USE_SOCKET_FALLBACK */