/* socket.c BSD socket interface code... */ /* * Copyright (c) 1996-1999 Internet Software Consortium. * Use is subject to license terms which appear in the file named * ISC-LICENSE that should have accompanied this file when you * received it. If a file named ISC-LICENSE did not accompany this * file, or you are not sure the one you have is correct, you may * obtain an applicable copy of the license at: * * http://www.isc.org/isc-license-1.0.html. * * This file is part of the ISC DHCP distribution. The documentation * associated with this file is listed in the file DOCUMENTATION, * included in the top-level directory of this release. * * Support and other services are available for ISC products - see * http://www.isc.org for more information. */ /* 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.36 1999/05/06 20:21:39 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) log_fatal ("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) log_fatal ("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) log_fatal ("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) log_fatal ("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) log_fatal ("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) { log_fatal ("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) log_info ("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) log_info ("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) { log_error ("send_packet: %m"); if (errno == ENETUNREACH) log_error ("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, 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) log_error ("fallback_discard: %m"); } #endif /* USE_SOCKET_SEND */ #if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) int can_unicast_without_arp (ip) struct interface_info *ip; { return 0; } /* On many stacks, we should return 1 here. */ int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 0; } /* 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 */