diff --git a/ndhc/arp.c b/ndhc/arp.c index bf349a2..7c5c9b0 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -30,10 +30,10 @@ #include #include #include +#include #include #include "arp.h" #include "packet.h" -#include "socket.h" #include "sys.h" #include "ifchange.h" #include "leasefile.h" @@ -81,7 +81,7 @@ static int arpping(struct client_state_t *cs, uint32_t test_ip, close(arpfd); return -1; } - set_sock_nonblock(arpfd); + fcntl(arpfd, F_SETFL, fcntl(arpfd, F_GETFL) | O_NONBLOCK); cs->arpFd = arpfd; epoll_add(cs, arpfd); } diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index c187fda..b6a55fd 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -45,7 +45,6 @@ #include "timeout.h" #include "sys.h" #include "ifchange.h" -#include "socket.h" #include "arp.h" #include "netlink.h" #include "leasefile.h" diff --git a/ndhc/packet.c b/ndhc/packet.c index c89ddbc..0d72076 100644 --- a/ndhc/packet.c +++ b/ndhc/packet.c @@ -3,6 +3,7 @@ * * (c) 2004-2011 Nicholas J. Kain * (c) 2001 Russ Dill + * Kernel BPF filter is (c) 2006, 2007 Stefan Rompf . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -31,18 +33,133 @@ #include #include #include +#include #include #include #include "packet.h" -#include "socket.h" #include "arp.h" #include "ifchange.h" #include "sys.h" #include "log.h" #include "io.h" #include "options.h" +#include "strl.h" +static int set_sock_nonblock(int fd) +{ + int ret = 0, flags; + flags = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + return ret; +} + +/* Returns fd of new listen socket bound to @ip:@port on interface @inf + * on success, or -1 on failure. */ +static int create_udp_listen_socket(unsigned int ip, int port, char *inf) +{ + struct ifreq interface; + int fd; + struct sockaddr_in addr; + int opt = 1; + + log_line("Opening listen socket on 0x%08x:%d %s", ip, port, inf); + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + log_error("create_udp_listen_socket: socket failed: %s", strerror(errno)); + goto out; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) + goto out_fd; + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof opt) == -1) + goto out_fd; + + /* Restrict operations to the physical device @inf */ + strlcpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + &interface, sizeof interface) < 0) + goto out_fd; + + set_sock_nonblock(fd); + + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) + goto out_fd; + + return fd; + out_fd: + close(fd); + out: + return -1; +} + +static int create_raw_listen_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + /* + * Comment: + * I've selected not to see LL header, so BPF doesn't see it, too. + * The filter may also pass non-IP and non-ARP packets, but we do + * a more complete check when receiving the message in userspace. + * and filter shamelessly stolen from: + * http://www.flamewarmaster.de/software/dhcpclient/ + * + * Copyright: 2006, 2007 Stefan Rompf . + * License: GPL v2. + */ +#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68) + static const struct sock_filter filter_instr[] = { + /* check for udp */ + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ + /* ugly check for arp on ethernet-like and IPv4 */ + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ + /* skip IP header */ + BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ + /* check udp source and destination ports */ + BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),/* L3, L4 */ + /* returns */ + BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ + BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ + }; + static const struct sock_fprog filter_prog = { + .len = sizeof(filter_instr) / sizeof(filter_instr[0]), + /* casting const away: */ + .filter = (struct sock_filter *) filter_instr, + }; + + memset(&sock, 0, sizeof sock); + log_line("Opening raw socket on ifindex %d", ifindex); + if ((fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + log_error("socket call failed: %s", strerror(errno)); + return -1; + } + + /* Ignoring error (kernel may lack support for this) */ + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, + sizeof filter_prog) >= 0) + log_line("Attached filter to raw socket fd %d", fd); + + set_sock_nonblock(fd); + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_IP); + sock.sll_ifindex = ifindex; + if (bind(fd, (struct sockaddr *)&sock, sizeof(sock)) < 0) { + log_error("bind call failed: %s", strerror(errno)); + close(fd); + return -1; + } + + return fd; +} // Read a packet from a cooked socket. Returns -1 on fatal error, -2 on // transient error. static int get_packet(struct dhcpMessage *packet, int fd) @@ -294,10 +411,11 @@ void change_listen_mode(struct client_state_t *cs, int new_mode) log_line("Stopped listening for DHCP packets."); return; case LM_RAW: - cs->listenFd = raw_socket(client_config.ifindex); + cs->listenFd = create_raw_listen_socket(client_config.ifindex); break; case LM_KERNEL: - cs->listenFd = listen_socket(INADDR_ANY, DHCP_CLIENT_PORT, + cs->listenFd = + create_udp_listen_socket(INADDR_ANY, DHCP_CLIENT_PORT, client_config.interface); break; } diff --git a/ndhc/socket.c b/ndhc/socket.c deleted file mode 100644 index 1dee9b7..0000000 --- a/ndhc/socket.c +++ /dev/null @@ -1,151 +0,0 @@ -/* socket.c - raw and kernel socket creation functions - * Time-stamp: <2011-03-30 23:47:03 nk> - * - * (c) 2004-2011 Nicholas J. Kain - * Kernel BPF filter is (c) 2006, 2007 Stefan Rompf . - * - * This program 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "strl.h" - -int set_sock_nonblock(int fd) -{ - int ret = 0, flags; - flags = fcntl(fd, F_GETFL); - ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); - return ret; -} - -/* Returns fd of new listen socket bound to @ip:@port on interface @inf - * on success, or -1 on failure. */ -int listen_socket(unsigned int ip, int port, char *inf) -{ - struct ifreq interface; - int fd; - struct sockaddr_in addr; - int opt = 1; - - log_line("Opening listen socket on 0x%08x:%d %s", ip, port, inf); - if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - log_error("listen_socket: socket failed: %s", strerror(errno)); - goto out; - } - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) - goto out_fd; - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof opt) == -1) - goto out_fd; - - /* Restrict operations to the physical device @inf */ - strlcpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, - &interface, sizeof interface) < 0) - goto out_fd; - - set_sock_nonblock(fd); - - memset(&addr, 0, sizeof addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) - goto out_fd; - - return fd; - out_fd: - close(fd); - out: - return -1; -} - -int raw_socket(int ifindex) -{ - int fd; - struct sockaddr_ll sock; - - /* - * Comment: - * I've selected not to see LL header, so BPF doesn't see it, too. - * The filter may also pass non-IP and non-ARP packets, but we do - * a more complete check when receiving the message in userspace. - * and filter shamelessly stolen from: - * http://www.flamewarmaster.de/software/dhcpclient/ - * - * Copyright: 2006, 2007 Stefan Rompf . - * License: GPL v2. - */ -#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68) - static const struct sock_filter filter_instr[] = { - /* check for udp */ - BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ - /* ugly check for arp on ethernet-like and IPv4 */ - BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ - /* skip IP header */ - BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ - /* check udp source and destination ports */ - BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),/* L3, L4 */ - /* returns */ - BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ - BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ - }; - static const struct sock_fprog filter_prog = { - .len = sizeof(filter_instr) / sizeof(filter_instr[0]), - /* casting const away: */ - .filter = (struct sock_filter *) filter_instr, - }; - - memset(&sock, 0, sizeof sock); - log_line("Opening raw socket on ifindex %d", ifindex); - if ((fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { - log_error("socket call failed: %s", strerror(errno)); - return -1; - } - - /* Ignoring error (kernel may lack support for this) */ - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, - sizeof filter_prog) >= 0) - log_line("Attached filter to raw socket fd %d", fd); - - set_sock_nonblock(fd); - - sock.sll_family = AF_PACKET; - sock.sll_protocol = htons(ETH_P_IP); - sock.sll_ifindex = ifindex; - if (bind(fd, (struct sockaddr *)&sock, sizeof(sock)) < 0) { - log_error("bind call failed: %s", strerror(errno)); - close(fd); - return -1; - } - - return fd; -} diff --git a/ndhc/socket.h b/ndhc/socket.h deleted file mode 100644 index e725f29..0000000 --- a/ndhc/socket.h +++ /dev/null @@ -1,30 +0,0 @@ -/* socket.h - raw and kernel socket creation functions - * Time-stamp: <2011-03-30 23:47:34 nk> - * - * (c) 2004-2011 Nicholas J. Kain - * - * This program 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. - */ - -#ifndef SOCKET_H_ -#define SOCKET_H_ - -#include - -int set_sock_nonblock(int fd); -int listen_socket(unsigned int ip, int port, char *inf); -int raw_socket(int ifindex); - -#endif