2006-11-28 04:23:22 +08:00
|
|
|
/*
|
2007-01-17 01:54:07 +08:00
|
|
|
* Copyright 2006-2007 Roy Marples <uberlord@gentoo.org>
|
2006-11-28 04:23:22 +08:00
|
|
|
* although a lot was lifted from udhcp
|
|
|
|
*
|
|
|
|
* dhcpcd is an RFC2131 compliant DHCP client daemon.
|
|
|
|
*
|
|
|
|
* This is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
2007-09-04 20:48:40 +08:00
|
|
|
#define _BSD_SOURCE
|
|
|
|
|
2006-12-15 07:17:27 +08:00
|
|
|
#include <sys/types.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <sys/ioctl.h>
|
2007-02-21 18:30:50 +08:00
|
|
|
#include <sys/select.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/uio.h>
|
2007-04-27 00:16:56 +08:00
|
|
|
#include <net/if.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <netinet/in_systm.h>
|
2007-04-05 23:01:50 +08:00
|
|
|
#include <netinet/in.h>
|
2007-04-09 01:35:22 +08:00
|
|
|
#ifndef __OpenBSD__
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <net/ethernet.h>
|
2007-04-09 01:35:22 +08:00
|
|
|
#endif
|
|
|
|
#include <netinet/udp.h>
|
2007-04-05 23:01:50 +08:00
|
|
|
#include <arpa/inet.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
2007-10-11 21:26:16 +08:00
|
|
|
#include <stdlib.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "dhcp.h"
|
|
|
|
#include "interface.h"
|
|
|
|
#include "logger.h"
|
2006-12-15 07:17:27 +08:00
|
|
|
#include "socket.h"
|
2006-11-28 04:23:22 +08:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
|
|
|
static uint16_t checksum (unsigned char *addr, uint16_t len)
|
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
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;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void make_dhcp_packet(struct udp_dhcp_packet *packet,
|
2007-04-11 21:18:33 +08:00
|
|
|
const unsigned char *data, int length,
|
|
|
|
struct in_addr source, struct in_addr dest)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
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 (struct udphdr) + length);
|
|
|
|
ip->ip_len = udp->uh_ulen;
|
|
|
|
udp->uh_sum = checksum ((unsigned char *) packet,
|
|
|
|
sizeof (struct udp_dhcp_packet));
|
|
|
|
|
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
ip->ip_hl = 5;
|
|
|
|
ip->ip_id = 0;
|
|
|
|
ip->ip_tos = IPTOS_LOWDELAY;
|
|
|
|
ip->ip_len = htons (sizeof (struct ip) + sizeof (struct udphdr) +
|
|
|
|
length);
|
|
|
|
ip->ip_id = 0;
|
|
|
|
ip->ip_off = 0;
|
|
|
|
ip->ip_ttl = IPDEFTTL;
|
|
|
|
|
|
|
|
ip->ip_sum = checksum ((unsigned char *) ip, sizeof (struct ip));
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
2006-12-15 22:47:01 +08:00
|
|
|
static int valid_dhcp_packet (unsigned char *data)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
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 (struct ip)))
|
|
|
|
{
|
|
|
|
logger (LOG_DEBUG, "bad IP header checksum, ignoring");
|
|
|
|
retval = -1;
|
|
|
|
goto eexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy (&source, &d.packet->ip.ip_src, sizeof (struct in_addr));
|
|
|
|
memcpy (&dest, &d.packet->ip.ip_dst, sizeof (struct in_addr));
|
|
|
|
memset (&d.packet->ip, 0, sizeof (struct ip));
|
|
|
|
d.packet->udp.uh_sum = 0;
|
|
|
|
|
|
|
|
d.packet->ip.ip_p = IPPROTO_UDP;
|
|
|
|
memcpy (&d.packet->ip.ip_src, &source, sizeof (struct in_addr));
|
|
|
|
memcpy (&d.packet->ip.ip_dst, &dest, sizeof (struct in_addr));
|
|
|
|
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;
|
|
|
|
}
|
2006-11-28 04:23:22 +08:00
|
|
|
|
|
|
|
eexit:
|
2007-04-11 21:18:33 +08:00
|
|
|
d.packet->ip.ip_sum = ipsum;
|
|
|
|
d.packet->ip.ip_len = iplen;
|
|
|
|
d.packet->udp.uh_sum = udpsum;
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
return retval;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
2006-12-19 18:06:21 +08:00
|
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) \
|
2007-04-11 21:18:33 +08:00
|
|
|
|| defined(__APPLE__)
|
2006-11-28 04:23:22 +08:00
|
|
|
/* Credit where credit is due :)
|
|
|
|
The below BPF filter is taken from ISC DHCP */
|
|
|
|
|
|
|
|
# include <net/bpf.h>
|
|
|
|
|
|
|
|
static struct bpf_insn dhcp_bpf_filter [] = {
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* Get the IP header length... */
|
|
|
|
BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* If we passed all the tests, ask for the whole packet. */
|
|
|
|
BPF_STMT (BPF_RET+BPF_K, (u_int) - 1),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* Otherwise, drop it. */
|
|
|
|
BPF_STMT (BPF_RET+BPF_K, 0),
|
2006-11-28 04:23:22 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct bpf_insn arp_bpf_filter [] = {
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* 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),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* If we passed all the tests, ask for the whole packet. */
|
|
|
|
BPF_STMT (BPF_RET+BPF_K, (u_int) - 1),
|
2006-11-28 04:23:22 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
/* Otherwise, drop it. */
|
|
|
|
BPF_STMT (BPF_RET+BPF_K, 0),
|
2006-11-28 04:23:22 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int open_socket (interface_t *iface, bool arp)
|
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
int n = 0;
|
|
|
|
int fd = -1;
|
2007-10-11 21:26:16 +08:00
|
|
|
char *device;
|
2007-04-11 21:18:33 +08:00
|
|
|
int flags;
|
|
|
|
struct ifreq ifr;
|
|
|
|
int buf = 0;
|
|
|
|
struct bpf_program p;
|
|
|
|
|
2007-10-11 21:26:16 +08:00
|
|
|
device = xmalloc (sizeof (char *) * PATH_MAX);
|
2007-04-11 21:18:33 +08:00
|
|
|
do {
|
|
|
|
snprintf (device, PATH_MAX, "/dev/bpf%d", n++);
|
|
|
|
fd = open (device, O_RDWR);
|
2007-07-18 19:35:50 +08:00
|
|
|
} while (fd == -1 && errno == EBUSY);
|
2007-10-11 21:26:16 +08:00
|
|
|
free (device);
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if (fd == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "unable to open a BPF device");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if ((flags = fcntl (fd, F_GETFD, 0)) == -1
|
|
|
|
|| fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
2007-04-11 21:18:33 +08:00
|
|
|
{
|
|
|
|
logger (LOG_ERR, "fcntl: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset (&ifr, 0, sizeof (struct ifreq));
|
|
|
|
strlcpy (ifr.ifr_name, iface->name, sizeof (ifr.ifr_name));
|
2007-07-18 19:35:50 +08:00
|
|
|
if (ioctl (fd, BIOCSETIF, &ifr) == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
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. */
|
2007-07-18 19:35:50 +08:00
|
|
|
if (ioctl (fd, BIOCGBLEN, &buf) == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "ioctl BIOCGBLEN: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iface->buffer_length = buf;
|
|
|
|
|
|
|
|
flags = 1;
|
2007-07-18 19:35:50 +08:00
|
|
|
if (ioctl (fd, BIOCIMMEDIATE, &flags) == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "ioctl BIOCIMMEDIATE: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Install the DHCP filter */
|
|
|
|
if (arp) {
|
|
|
|
p.bf_insns = arp_bpf_filter;
|
|
|
|
p.bf_len = sizeof (arp_bpf_filter) / sizeof (struct bpf_insn);
|
|
|
|
} else {
|
|
|
|
p.bf_insns = dhcp_bpf_filter;
|
|
|
|
p.bf_len = sizeof (dhcp_bpf_filter) / sizeof (struct bpf_insn);
|
|
|
|
}
|
2007-07-18 19:35:50 +08:00
|
|
|
if (ioctl (fd, BIOCSETF, &p) == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "ioctl BIOCSETF: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iface->fd > -1)
|
|
|
|
close (iface->fd);
|
|
|
|
iface->fd = fd;
|
|
|
|
|
|
|
|
return fd;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
2006-12-15 08:00:18 +08:00
|
|
|
int send_packet (const interface_t *iface, int type,
|
2007-04-11 21:18:33 +08:00
|
|
|
const unsigned char *data, int len)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
int retval = -1;
|
|
|
|
struct iovec iov[2];
|
|
|
|
|
|
|
|
if (iface->family == ARPHRD_ETHER) {
|
|
|
|
struct ether_header hw;
|
|
|
|
memset (&hw, 0, sizeof (struct ether_header));
|
|
|
|
memset (&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
|
|
|
|
hw.ether_type = htons (type);
|
|
|
|
|
|
|
|
iov[0].iov_base = &hw;
|
|
|
|
iov[0].iov_len = sizeof (struct ether_header);
|
|
|
|
} 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;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* BPF requires that we read the entire buffer.
|
|
|
|
So we pass the buffer in the API so we can loop on >1 dhcp packet. */
|
2006-12-15 08:00:18 +08:00
|
|
|
int get_packet (const interface_t *iface, unsigned char *data,
|
2007-04-11 21:18:33 +08:00
|
|
|
unsigned char *buffer, int *buffer_len, int *buffer_pos)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
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 timeval tv;
|
|
|
|
logger (LOG_ERR, "read: %s", strerror (errno));
|
|
|
|
tv.tv_sec = 3;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
select (0, NULL, NULL, NULL, &tv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
bpf.buffer += *buffer_pos;
|
|
|
|
|
|
|
|
while (bpf.packet) {
|
|
|
|
int len = -1;
|
|
|
|
union
|
|
|
|
{
|
|
|
|
unsigned char *buffer;
|
|
|
|
struct ether_header *hw;
|
|
|
|
} hdr;
|
|
|
|
unsigned char *payload;
|
|
|
|
|
|
|
|
/* 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 (struct ether_header);
|
|
|
|
|
|
|
|
/* 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 (struct ether_header);
|
|
|
|
memcpy (data, payload, len);
|
|
|
|
} 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 (struct ip) -
|
|
|
|
sizeof (struct udphdr);
|
|
|
|
memcpy (data, &pay.packet->dhcp, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the buffer_pos pointer */
|
|
|
|
bpf.buffer +=
|
|
|
|
BPF_WORDALIGN (bpf.packet->bh_hdrlen + bpf.packet->bh_caplen);
|
|
|
|
if (bpf.buffer - buffer < *buffer_len)
|
|
|
|
*buffer_pos = bpf.buffer - buffer;
|
|
|
|
else
|
|
|
|
*buffer_pos = 0;
|
|
|
|
|
|
|
|
if (len != -1)
|
|
|
|
return len;
|
|
|
|
|
|
|
|
if (*buffer_pos == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No valid packets left, so return */
|
|
|
|
*buffer_pos = 0;
|
|
|
|
return -1;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#elif __linux__
|
|
|
|
|
|
|
|
#include <netpacket/packet.h>
|
|
|
|
|
|
|
|
int open_socket (interface_t *iface, bool arp)
|
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
int fd;
|
|
|
|
int flags;
|
2007-07-21 21:00:18 +08:00
|
|
|
union sockunion {
|
|
|
|
struct sockaddr sa;
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
struct sockaddr_storage ss;
|
|
|
|
} su;
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if ((fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_IP))) == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "socket: %s", strerror (errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if ((flags = fcntl (fd, F_GETFD, 0)) == -1
|
|
|
|
|| fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
2007-04-11 21:18:33 +08:00
|
|
|
{
|
|
|
|
logger (LOG_ERR, "fcntl: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-21 21:00:18 +08:00
|
|
|
memset (&su, 0, sizeof (struct sockaddr_storage));
|
|
|
|
su.sll.sll_family = AF_PACKET;
|
2007-04-11 21:18:33 +08:00
|
|
|
if (arp)
|
2007-07-21 21:00:18 +08:00
|
|
|
su.sll.sll_protocol = htons (ETH_P_ARP);
|
2007-04-11 21:18:33 +08:00
|
|
|
else
|
2007-07-21 21:00:18 +08:00
|
|
|
su.sll.sll_protocol = htons (ETH_P_IP);
|
|
|
|
if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "if_nametoindex: Couldn't find index for interface `%s'",
|
|
|
|
iface->name);
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-21 21:00:18 +08:00
|
|
|
if (bind (fd, &su.sa, sizeof (struct sockaddr_storage)) == -1)
|
2007-04-11 21:18:33 +08:00
|
|
|
{
|
|
|
|
logger (LOG_ERR, "bind: %s", strerror (errno));
|
|
|
|
close (fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iface->fd > -1)
|
|
|
|
close (iface->fd);
|
|
|
|
iface->fd = fd;
|
2007-07-21 21:00:18 +08:00
|
|
|
iface->socket_protocol = ntohs (su.sll.sll_protocol);
|
2007-04-11 21:18:33 +08:00
|
|
|
|
|
|
|
iface->buffer_length = BUFFER_LENGTH;
|
|
|
|
|
|
|
|
return fd;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
2006-12-15 07:47:56 +08:00
|
|
|
int send_packet (const interface_t *iface, const int type,
|
2007-04-11 21:18:33 +08:00
|
|
|
const unsigned char *data, const int len)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-07-21 21:00:18 +08:00
|
|
|
union sockunion {
|
|
|
|
struct sockaddr sa;
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
struct sockaddr_storage ss;
|
|
|
|
} su;
|
2007-04-11 21:18:33 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (! iface)
|
|
|
|
return -1;
|
|
|
|
|
2007-07-21 21:00:18 +08:00
|
|
|
memset (&su, 0, sizeof (struct sockaddr_storage));
|
|
|
|
su.sll.sll_family = AF_PACKET;
|
|
|
|
su.sll.sll_protocol = htons (type);
|
|
|
|
if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) {
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "if_nametoindex: Couldn't find index for interface `%s'",
|
|
|
|
iface->name);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-07-21 21:00:18 +08:00
|
|
|
su.sll.sll_halen = iface->hwlen;
|
|
|
|
memset(&su.sll.sll_addr, 0xff, iface->hwlen);
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-07-21 21:00:18 +08:00
|
|
|
if ((retval = sendto (iface->fd, data, len, 0, &su.sa,
|
|
|
|
sizeof (struct sockaddr_storage))) == -1)
|
2007-04-11 21:18:33 +08:00
|
|
|
|
|
|
|
logger (LOG_ERR, "sendto: %s", strerror (errno));
|
|
|
|
return retval;
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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. */
|
2006-12-15 07:47:56 +08:00
|
|
|
int get_packet (const interface_t *iface, unsigned char *data,
|
2007-04-11 21:18:33 +08:00
|
|
|
unsigned char *buffer, int *buffer_len, int *buffer_pos)
|
2006-11-28 04:23:22 +08:00
|
|
|
{
|
2007-04-11 21:18:33 +08:00
|
|
|
long 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);
|
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if (bytes == -1) {
|
2007-04-11 21:18:33 +08:00
|
|
|
struct timeval tv;
|
|
|
|
logger (LOG_ERR, "read: %s", strerror (errno));
|
|
|
|
tv.tv_sec = 3;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
select (0, NULL, NULL, NULL, &tv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*buffer_len = bytes;
|
|
|
|
/* If it's an ARP reply, then just send it back */
|
|
|
|
if (iface->socket_protocol == ETH_P_ARP) {
|
|
|
|
memcpy (data, buffer, bytes);
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((unsigned) bytes < (sizeof (struct ip) + sizeof (struct udphdr))) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes = ntohs (pay.packet->ip.ip_len);
|
|
|
|
|
|
|
|
/* This is like our BPF filter above */
|
|
|
|
if (pay.packet->ip.ip_p != IPPROTO_UDP || pay.packet->ip.ip_v != IPVERSION ||
|
|
|
|
pay.packet->ip.ip_hl != sizeof (pay.packet->ip) >> 2 ||
|
|
|
|
pay.packet->udp.uh_dport != htons (DHCP_CLIENT_PORT) ||
|
|
|
|
bytes > (int) sizeof (struct udp_dhcp_packet) ||
|
|
|
|
ntohs (pay.packet->udp.uh_ulen)
|
|
|
|
!= (uint16_t) (bytes - sizeof (pay.packet->ip)))
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-18 19:35:50 +08:00
|
|
|
if (valid_dhcp_packet (buffer) == -1)
|
2007-04-11 21:18:33 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(data, &pay.packet->dhcp,
|
|
|
|
bytes - (sizeof (pay.packet->ip) + sizeof (pay.packet->udp)));
|
|
|
|
|
|
|
|
return bytes - (sizeof (pay.packet->ip) + sizeof (pay.packet->udp));
|
2006-11-28 04:23:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#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
|