diff options
Diffstat (limited to 'workspace/networkDiscovery/dhcpcd/socket.c')
-rw-r--r-- | workspace/networkDiscovery/dhcpcd/socket.c | 647 |
1 files changed, 0 insertions, 647 deletions
diff --git a/workspace/networkDiscovery/dhcpcd/socket.c b/workspace/networkDiscovery/dhcpcd/socket.c deleted file mode 100644 index 58ad6c5..0000000 --- a/workspace/networkDiscovery/dhcpcd/socket.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * dhcpcd - DHCP client daemon - * Copyright 2006-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <net/if.h> -#include <netinet/in_systm.h> -#include <netinet/in.h> -#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */ -#include <netinet/udp.h> -#undef __FAVOR_BSD -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#if defined(BSD) || defined(__FreeBSD_kernel__) -# include <net/bpf.h> -#elif __linux__ -# include <linux/filter.h> -# include <netpacket/packet.h> -# define bpf_insn sock_filter -#endif - -#include "config.h" -#include "dhcp.h" -#include "interface.h" -#include "logger.h" -#include "socket.h" - -/* A suitably large buffer for all transactions. - * BPF buffer size is set by the kernel, so no define. */ -#ifdef __linux__ -# define BUFFER_LENGTH 4096 -#endif - -/* Broadcast address for IPoIB */ -static const uint8_t ipv4_bcast_addr[] = { - 0x00, 0xff, 0xff, 0xff, - 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff -}; - -/* Credit where credit is due :) - * The below BPF filter is taken from ISC DHCP */ -static struct bpf_insn dhcp_bpf_filter [] = { - /* Make sure this is an IP packet... */ - BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), - BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), - - /* Make sure it's a UDP packet... */ - BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), - BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), - - /* Make sure this isn't a fragment... */ - BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20), - BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), - - /* Get the IP header length... */ - BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), - - /* Make sure it's to the right port... */ - BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), - BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1), - - /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT (BPF_RET + BPF_K, ~0U), - - /* Otherwise, drop it. */ - BPF_STMT (BPF_RET + BPF_K, 0), -}; - -static struct bpf_insn arp_bpf_filter [] = { - /* Make sure this is an ARP packet... */ - BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), - BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3), - - /* Make sure this is an ARP REPLY... */ - BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20), - BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), - - /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT (BPF_RET + BPF_K, ~0U), - - /* Otherwise, drop it. */ - BPF_STMT (BPF_RET + BPF_K, 0), -}; - -void setup_packet_filters (void) -{ -#ifdef __linux__ - /* We need to massage the filters for Linux cooked packets */ - dhcp_bpf_filter[1].jf = 0; /* skip 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; - - arp_bpf_filter[1].jf = 0; /* skip the IP packet type check */ - arp_bpf_filter[2].k -= ETH_HLEN; -#endif -} - -static uint16_t checksum (unsigned char *addr, uint16_t len) -{ - uint32_t sum = 0; - union - { - unsigned char *addr; - uint16_t *i; - } p; - uint16_t nleft = len; - - p.addr = addr; - while (nleft > 1) { - sum += *p.i++; - nleft -= 2; - } - - if (nleft == 1) { - uint8_t a = 0; - memcpy (&a, p.i, 1); - sum += ntohs (a) << 8; - } - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - - return ~sum; -} - -void make_dhcp_packet(struct udp_dhcp_packet *packet, - const unsigned char *data, size_t length, - struct in_addr source, struct in_addr dest) -{ - struct ip *ip = &packet->ip; - struct udphdr *udp = &packet->udp; - - /* OK, this is important :) - * We copy the data to our packet and then create a small part of the - * ip structure and an invalid ip_len (basically udp length). - * We then fill the udp structure and put the checksum - * of the whole packet into the udp checksum. - * Finally we complete the ip structure and ip checksum. - * If we don't do the ordering like so then the udp checksum will be - * broken, so find another way of doing it! */ - - memcpy (&packet->dhcp, data, length); - - ip->ip_p = IPPROTO_UDP; - ip->ip_src.s_addr = source.s_addr; - if (dest.s_addr == 0) - ip->ip_dst.s_addr = INADDR_BROADCAST; - else - ip->ip_dst.s_addr = dest.s_addr; - - udp->uh_sport = htons (DHCP_CLIENT_PORT); - udp->uh_dport = htons (DHCP_SERVER_PORT); - udp->uh_ulen = htons (sizeof (*udp) + length); - ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum ((unsigned char *) packet, sizeof (*packet)); - - ip->ip_v = IPVERSION; - ip->ip_hl = 5; - ip->ip_id = 0; - ip->ip_tos = IPTOS_LOWDELAY; - ip->ip_len = htons (sizeof (*ip) + sizeof (*udp) + length); - ip->ip_id = 0; - ip->ip_off = htons (IP_DF); /* Don't fragment */ - ip->ip_ttl = IPDEFTTL; - - ip->ip_sum = checksum ((unsigned char *) ip, sizeof (*ip)); -} - -static int valid_dhcp_packet (unsigned char *data) -{ - union - { - unsigned char *data; - struct udp_dhcp_packet *packet; - } d; - uint16_t bytes; - uint16_t ipsum; - uint16_t iplen; - uint16_t udpsum; - struct in_addr source; - struct in_addr dest; - int retval = 0; - - d.data = data; - bytes = ntohs (d.packet->ip.ip_len); - ipsum = d.packet->ip.ip_sum; - iplen = d.packet->ip.ip_len; - udpsum = d.packet->udp.uh_sum; - - d.data = data; - d.packet->ip.ip_sum = 0; - if (ipsum != checksum ((unsigned char *) &d.packet->ip, - sizeof (d.packet->ip))) - { - logger (LOG_DEBUG, "bad IP header checksum, ignoring"); - retval = -1; - goto eexit; - } - - memcpy (&source, &d.packet->ip.ip_src, sizeof (d.packet->ip.ip_src)); - memcpy (&dest, &d.packet->ip.ip_dst, sizeof (d.packet->ip.ip_dst)); - memset (&d.packet->ip, 0, sizeof (d.packet->ip)); - d.packet->udp.uh_sum = 0; - - d.packet->ip.ip_p = IPPROTO_UDP; - memcpy (&d.packet->ip.ip_src, &source, sizeof (d.packet->ip.ip_src)); - memcpy (&d.packet->ip.ip_dst, &dest, sizeof (d.packet->ip.ip_dst)); - d.packet->ip.ip_len = d.packet->udp.uh_ulen; - if (udpsum && udpsum != checksum (d.data, bytes)) { - logger (LOG_ERR, "bad UDP checksum, ignoring"); - retval = -1; - } - -eexit: - d.packet->ip.ip_sum = ipsum; - d.packet->ip.ip_len = iplen; - d.packet->udp.uh_sum = udpsum; - - return retval; -} - -#if defined(BSD) || defined(__FreeBSD_kernel__) -int open_socket (interface_t *iface, int protocol) -{ - int n = 0; - int fd = -1; - char *device; - int flags; - struct ifreq ifr; - int buf = 0; - struct bpf_program pf; - - device = xmalloc (sizeof (char) * PATH_MAX); - do { - snprintf (device, PATH_MAX, "/dev/bpf%d", n++); - fd = open (device, O_RDWR); - } while (fd == -1 && errno == EBUSY); - free (device); - - if (fd == -1) { - logger (LOG_ERR, "unable to open a BPF device"); - return -1; - } - - close_on_exec (fd); - - memset (&ifr, 0, sizeof (ifr)); - strlcpy (ifr.ifr_name, iface->name, sizeof (ifr.ifr_name)); - if (ioctl (fd, BIOCSETIF, &ifr) == -1) { - logger (LOG_ERR, - "cannot attach interface `%s' to bpf device `%s': %s", - iface->name, device, strerror (errno)); - close (fd); - return -1; - } - - /* Get the required BPF buffer length from the kernel. */ - if (ioctl (fd, BIOCGBLEN, &buf) == -1) { - logger (LOG_ERR, "ioctl BIOCGBLEN: %s", strerror (errno)); - close (fd); - return -1; - } - iface->buffer_length = buf; - - flags = 1; - if (ioctl (fd, BIOCIMMEDIATE, &flags) == -1) { - logger (LOG_ERR, "ioctl BIOCIMMEDIATE: %s", strerror (errno)); - close (fd); - return -1; - } - - /* Install the DHCP filter */ - if (protocol == ETHERTYPE_ARP) { - pf.bf_insns = arp_bpf_filter; - pf.bf_len = sizeof (arp_bpf_filter) - / sizeof (arp_bpf_filter[0]); - } else { - pf.bf_insns = dhcp_bpf_filter; - pf.bf_len = sizeof (dhcp_bpf_filter) - / sizeof (dhcp_bpf_filter[0]); - } - if (ioctl (fd, BIOCSETF, &pf) == -1) { - logger (LOG_ERR, "ioctl BIOCSETF: %s", strerror (errno)); - close (fd); - return -1; - } - - if (iface->fd > -1) - close (iface->fd); - iface->fd = fd; - - return fd; -} - -ssize_t send_packet (const interface_t *iface, int type, - const unsigned char *data, size_t len) -{ - ssize_t retval = -1; - struct iovec iov[2]; - - if (iface->family == ARPHRD_ETHER) { - struct ether_header hw; - memset (&hw, 0, sizeof (hw)); - memset (&hw.ether_dhost, 0xff, ETHER_ADDR_LEN); - hw.ether_type = htons (type); - - iov[0].iov_base = &hw; - iov[0].iov_len = sizeof (hw); - } else { - logger (LOG_ERR, "unsupported interace type %d", iface->family); - return -1; - } - iov[1].iov_base = (unsigned char *) data; - iov[1].iov_len = len; - - if ((retval = writev(iface->fd, iov, 2)) == -1) - logger (LOG_ERR, "writev: %s", strerror (errno)); - - return retval; -} - -/* BPF requires that we read the entire buffer. - * So we pass the buffer in the API so we can loop on >1 dhcp packet. */ -ssize_t get_packet (const interface_t *iface, unsigned char *data, - unsigned char *buffer, - size_t *buffer_len, size_t *buffer_pos) -{ - union - { - unsigned char *buffer; - struct bpf_hdr *packet; - } bpf; - - bpf.buffer = buffer; - - if (*buffer_pos < 1) { - memset (bpf.buffer, 0, iface->buffer_length); - *buffer_len = read (iface->fd, bpf.buffer, iface->buffer_length); - *buffer_pos = 0; - if (*buffer_len < 1) { - struct timespec ts; - logger (LOG_ERR, "read: %s", strerror (errno)); - ts.tv_sec = 3; - ts.tv_nsec = 0; - nanosleep (&ts, NULL); - return (-1); - } - } else - bpf.buffer += *buffer_pos; - - while (bpf.packet) { - size_t len = 0; - union - { - unsigned char *buffer; - struct ether_header *hw; - } hdr; - unsigned char *payload; - bool have_data = false; - - /* Ensure that the entire packet is in our buffer */ - if (*buffer_pos + bpf.packet->bh_hdrlen + bpf.packet->bh_caplen - > (unsigned) *buffer_len) - break; - - hdr.buffer = bpf.buffer + bpf.packet->bh_hdrlen; - payload = hdr.buffer + sizeof (*hdr.hw); - - /* If it's an ARP reply, then just send it back */ - if (hdr.hw->ether_type == htons (ETHERTYPE_ARP)) { - len = bpf.packet->bh_caplen - - sizeof (*hdr.hw); - memcpy (data, payload, len); - have_data = true; - } else { - if (valid_dhcp_packet (payload) >= 0) { - union - { - unsigned char *buffer; - struct udp_dhcp_packet *packet; - } pay; - pay.buffer = payload; - len = ntohs (pay.packet->ip.ip_len) - - sizeof (pay.packet->ip) - - sizeof (pay.packet->udp); - memcpy (data, &pay.packet->dhcp, len); - have_data = true; - } - } - - /* Update the buffer_pos pointer */ - bpf.buffer += BPF_WORDALIGN (bpf.packet->bh_hdrlen + - bpf.packet->bh_caplen); - if ((unsigned) (bpf.buffer - buffer) < *buffer_len) - *buffer_pos = bpf.buffer - buffer; - else - *buffer_pos = 0; - - if (have_data) - return len; - - if (*buffer_pos == 0) - break; - } - - /* No valid packets left, so return */ - *buffer_pos = 0; - return -1; -} - -#elif __linux__ - -int open_socket (interface_t *iface, int protocol) -{ - int fd; - union sockunion { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_ll sll; - struct sockaddr_storage ss; - } su; - struct sock_fprog pf; - struct ifreq ifr; - int n = 1; - - /* We need to bind to a port, otherwise Linux generate ICMP messages - * that cannot contect the port when we have an address. - * We don't actually use this fd at all, instead using our packet - * filter socket. */ - if (iface->listen_fd == -1 && protocol == ETHERTYPE_IP) { - if ((fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - logger (LOG_ERR, "socket: %s", strerror (errno)); - } else { - memset (&su, 0, sizeof (su)); - su.sin.sin_family = AF_INET; - su.sin.sin_port = htons (DHCP_CLIENT_PORT); - if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, - &n, sizeof (n)) == -1) - logger (LOG_ERR, "SO_REUSEADDR: %s", - strerror (errno)); - if (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, - &n, sizeof (n)) == -1) - logger (LOG_ERR, "SO_RCVBUF: %s", - strerror (errno)); - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, iface->name, - sizeof (ifr.ifr_name)); - if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, - &ifr, sizeof (ifr)) == -1) - logger (LOG_ERR, "SO_SOBINDTODEVICE: %s", - strerror (errno)); - if (bind (fd, &su.sa, sizeof (su)) == -1) { - logger (LOG_ERR, "bind: %s", strerror (errno)); - close (fd); - } else { - iface->listen_fd = fd; - close_on_exec (fd); - } - } - } - - if ((fd = socket (PF_PACKET, SOCK_DGRAM, htons (protocol))) == -1) { - logger (LOG_ERR, "socket: %s", strerror (errno)); - return (-1); - } - close_on_exec (fd); - - memset (&su, 0, sizeof (su)); - su.sll.sll_family = PF_PACKET; - su.sll.sll_protocol = htons (protocol); - if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) { - logger (LOG_ERR, - "if_nametoindex: no index for interface `%s'", - iface->name); - close (fd); - return (-1); - } - - /* Install the DHCP filter */ - memset (&pf, 0, sizeof (pf)); - if (protocol == ETHERTYPE_ARP) { - pf.filter = arp_bpf_filter; - pf.len = sizeof (arp_bpf_filter) / sizeof (arp_bpf_filter[0]); - } else { - pf.filter = dhcp_bpf_filter; - pf.len = sizeof (dhcp_bpf_filter) / sizeof (dhcp_bpf_filter[0]); - } - if (setsockopt (fd, SOL_SOCKET, SO_ATTACH_FILTER, - &pf, sizeof (pf)) != 0) - { - logger (LOG_ERR, "SO_ATTACH_FILTER: %s", strerror (errno)); - close (fd); - return (-1); - } - - if (bind (fd, &su.sa, sizeof (su)) == -1) { - logger (LOG_ERR, "bind: %s", strerror (errno)); - close (fd); - return (-1); - } - - if (iface->fd > -1) - close (iface->fd); - iface->fd = fd; - iface->socket_protocol = protocol; - iface->buffer_length = BUFFER_LENGTH; - - return (fd); -} - -ssize_t send_packet (const interface_t *iface, int type, - const unsigned char *data, size_t len) -{ - union sockunion { - struct sockaddr sa; - struct sockaddr_ll sll; - struct sockaddr_storage ss; - } su; - ssize_t retval; - - if (! iface) - return (-1); - - memset (&su, 0, sizeof (su)); - su.sll.sll_family = AF_PACKET; - su.sll.sll_protocol = htons (type); - - if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) { - logger (LOG_ERR, "if_nametoindex: no index for interface `%s'", - iface->name); - return (-1); - } - - su.sll.sll_hatype = htons (iface->family); - su.sll.sll_halen = iface->hwlen; - if (iface->family == ARPHRD_INFINIBAND) - memcpy (&su.sll.sll_addr, - &ipv4_bcast_addr, sizeof (ipv4_bcast_addr)); - else - memset (&su.sll.sll_addr, 0xff, iface->hwlen); - - if ((retval = sendto (iface->fd, data, len, 0, &su.sa, - sizeof (su))) == -1) - - logger (LOG_ERR, "sendto: %s", strerror (errno)); - return (retval); -} - -/* Linux has no need for the buffer as we can read as much as we want. - * We only have the buffer listed to keep the same API. */ -ssize_t get_packet (const interface_t *iface, unsigned char *data, - unsigned char *buffer, - size_t *buffer_len, size_t *buffer_pos) -{ - ssize_t bytes; - union - { - unsigned char *buffer; - struct udp_dhcp_packet *packet; - } pay; - - /* We don't use the given buffer, but we need to rewind the position */ - *buffer_pos = 0; - - memset (buffer, 0, iface->buffer_length); - bytes = read (iface->fd, buffer, iface->buffer_length); - - if (bytes == -1) { - struct timespec ts; - logger (LOG_ERR, "read: %s", strerror (errno)); - ts.tv_sec = 3; - ts.tv_nsec = 0; - nanosleep (&ts, NULL); - return (-1); - } - - *buffer_len = bytes; - /* If it's an ARP reply, then just send it back */ - if (iface->socket_protocol == ETHERTYPE_ARP) { - memcpy (data, buffer, bytes); - return (bytes); - } - - if ((unsigned) bytes < (sizeof (pay.packet->ip) + - sizeof (pay.packet->udp))) - { - logger (LOG_DEBUG, "message too short, ignoring"); - return (-1); - } - - pay.buffer = buffer; - if (bytes < ntohs (pay.packet->ip.ip_len)) { - logger (LOG_DEBUG, "truncated packet, ignoring"); - return (-1); - } - - if (valid_dhcp_packet (buffer) == -1) - return (-1); - - bytes = ntohs (pay.packet->ip.ip_len) - - (sizeof (pay.packet->ip) + sizeof (pay.packet->udp)); - memcpy (data, &pay.packet->dhcp, bytes); - return (bytes); -} - -#else - #error "Platform not supported!" - #error "We currently support BPF and Linux sockets." - #error "Other platforms may work using BPF. If yours does, please let me know" - #error "so I can add it to our list." -#endif |