From 6750209e126afc2909fc66592ad3397e427478bc Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Sat, 5 Apr 2014 21:39:27 -0400 Subject: [PATCH] Have sockd apply BPF filters to ARP sockets. --- ndhc/arp.c | 161 +++++++++++++++++---------------------------------- ndhc/sockd.c | 123 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 109 deletions(-) diff --git a/ndhc/arp.c b/ndhc/arp.c index 39bbf98..3d0ad9a 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -25,6 +25,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -123,95 +124,51 @@ void arp_reset_send_stats(void) } } -static void arp_set_bpf_basic(int fd) +static int get_arp_basic_socket(void) { - static struct sock_filter sf_arp[] = { - // Verify that the frame has ethernet protocol type of ARP - // and that the ARP hardware type field indicates Ethernet. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER, - 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - // Verify that the ARP protocol type field indicates IP, the ARP - // hardware address length field is 6, and the ARP protocol address - // length field is 4. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - // Sanity tests passed, so send all possible data. - BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), - }; - static const struct sock_fprog sfp_arp = { - .len = sizeof sf_arp / sizeof sf_arp[0], - .filter = sf_arp, - }; - using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, - sizeof sfp_arp) != -1; - if (!using_arp_bpf) - log_warning("%s: Failed to set BPF for ARP socket: %s", - client_config.interface, strerror(errno)); + char resp; + int fd = request_sockd_fd("a", 1, &resp); + switch (resp) { + case 'A': using_arp_bpf = 1; break; + case 'a': using_arp_bpf = 0; break; + default: suicide("%s: (%s) expected a or A sockd reply but got %c", + client_config.interface, __func__, resp); + } + return fd; } -static void arp_set_bpf_defense(struct client_state_t *cs, int fd) +static int get_arp_defense_socket(struct client_state_t *cs) { - uint32_t mac4b; - uint16_t mac2b; - memcpy(&mac4b, client_config.arp, 4); - memcpy(&mac2b, client_config.arp+4, 2); - - struct sock_filter sf_arp[] = { - // Verify that the frame has ethernet protocol type of ARP - // and that the ARP hardware type field indicates Ethernet. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER, - 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - // Verify that the ARP protocol type field indicates IP, the ARP - // hardware address length field is 6, and the ARP protocol address - // length field is 4. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - - // If the ARP packet source IP does not match our IP address, then - // it can be ignored. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 28), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cs->clientAddr, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - // If the first four bytes of the ARP packet source hardware address - // does not equal our hardware address, then it's a conflict and should - // be passed along. - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 22), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac4b, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), - // If the last two bytes of the ARP packet source hardware address - // do not equal our hardware address, then it's a conflict and should - // be passed along. - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 26), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac2b, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), - // Packet announces our IP address and hardware address, so it requires - // no action. - BPF_STMT(BPF_RET + BPF_K, 0), - }; - struct sock_fprog sfp_arp = { - .len = sizeof sf_arp / sizeof sf_arp[0], - .filter = (struct sock_filter *)sf_arp, - }; - using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, - sizeof sfp_arp) != -1; -} - -static int get_arp_socket(void) -{ - return request_sockd_fd("a", 1, NULL); + char buf[32]; + size_t buflen = 0; + buf[0] = 'd'; + buflen += 1; + memcpy(buf + buflen, &cs->clientAddr, sizeof cs->clientAddr); + buflen += sizeof cs->clientAddr; + memcpy(buf + buflen, client_config.arp, 6); + buflen += 6; + char resp; + int fd = request_sockd_fd(buf, buflen, &resp); + switch (resp) { + case 'D': using_arp_bpf = 1; break; + case 'd': using_arp_bpf = 0; break; + default: suicide("%s: (%s) expected d or D sockd reply but got %c", + client_config.interface, __func__, resp); + } + return fd; } static int arp_open_fd(struct client_state_t *cs) { if (cs->arpFd != -1) return 0; - cs->arpFd = get_arp_socket(); + switch (arpState) { + default: cs->arpFd = -1; arpState = AS_NONE; return -1; + case AS_COLLISION_CHECK: + case AS_GW_QUERY: + case AS_GW_CHECK: cs->arpFd = get_arp_basic_socket(); break; + case AS_DEFENSE: cs->arpFd = get_arp_defense_socket(cs); break; + } if (cs->arpFd == -1) { log_error("arp: Failed to create socket: %s", strerror(errno)); return -1; @@ -221,6 +178,17 @@ static int arp_open_fd(struct client_state_t *cs) return 0; } +static int arp_min_close_fd(struct client_state_t *cs) +{ + if (cs->arpFd == -1) + return 0; + epoll_del(cs->epollFd, cs->arpFd); + close(cs->arpFd); + cs->arpFd = -1; + arpState = AS_NONE; + return 1; +} + static void arp_switch_state(struct client_state_t *cs, arp_state_t state) { arp_state_t prev_state = arpState; @@ -231,32 +199,14 @@ static void arp_switch_state(struct client_state_t *cs, arp_state_t state) arp_close_fd(cs); return; } - if (cs->arpFd == -1) { + bool force_reopen = arpState == AS_DEFENSE || prev_state == AS_DEFENSE; + if (force_reopen) + arp_min_close_fd(cs); + if (cs->arpFd == -1 || force_reopen) { if (arp_open_fd(cs) == -1) - suicide("arp: Failed to open arpFd when changing state to %u", - arpState); - if (arpState != AS_DEFENSE) - arp_set_bpf_basic(cs->arpFd); + suicide("arp: Failed to open arpFd when changing state %u -> %u", + prev_state, arpState); } - if (arpState == AS_DEFENSE) { - arp_set_bpf_defense(cs, cs->arpFd); - return; - } - if (prev_state == AS_DEFENSE) { - arp_set_bpf_basic(cs->arpFd); - return; - } -} - -static int arp_min_close_fd(struct client_state_t *cs) -{ - if (cs->arpFd == -1) - return 0; - epoll_del(cs->epollFd, cs->arpFd); - close(cs->arpFd); - cs->arpFd = -1; - arpState = AS_NONE; - return 1; } int arp_close_fd(struct client_state_t *cs) @@ -271,11 +221,6 @@ static int arp_reopen_fd(struct client_state_t *cs) { arp_state_t prev_state = arpState; arp_min_close_fd(cs); - if (arp_open_fd(cs) == -1) { - log_warning("arp: Failed to re-open fd. Something is very wrong."); - log_warning("arp: Client will still run, but functionality will be degraded."); - return -1; - } arp_switch_state(cs, prev_state); return 0; } diff --git a/ndhc/sockd.c b/ndhc/sockd.c index 0f91b65..1b1e041 100644 --- a/ndhc/sockd.c +++ b/ndhc/sockd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -333,6 +335,108 @@ static int create_raw_broadcast_socket(void) return create_raw_socket(&da, NULL, NULL); } +static bool arp_set_bpf_basic(int fd) +{ + static struct sock_filter sf_arp[] = { + // Verify that the frame has ethernet protocol type of ARP + // and that the ARP hardware type field indicates Ethernet. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER, + 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // Verify that the ARP protocol type field indicates IP, the ARP + // hardware address length field is 6, and the ARP protocol address + // length field is 4. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // Sanity tests passed, so send all possible data. + BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), + }; + static const struct sock_fprog sfp_arp = { + .len = sizeof sf_arp / sizeof sf_arp[0], + .filter = sf_arp, + }; + int ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, + sizeof sfp_arp) != -1; + if (ret < 0) + log_warning("%s: Failed to set BPF for basic ARP socket: %s", + client_config.interface, strerror(errno)); + return ret == 0; +} + +static bool arp_set_bpf_defense(int fd, uint32_t client_addr, + uint8_t client_mac[6]) +{ + uint32_t mac4b; + uint16_t mac2b; + memcpy(&mac4b, client_mac, 4); + memcpy(&mac2b, client_mac + 4, 2); + + struct sock_filter sf_arp[] = { + // Verify that the frame has ethernet protocol type of ARP + // and that the ARP hardware type field indicates Ethernet. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER, + 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // Verify that the ARP protocol type field indicates IP, the ARP + // hardware address length field is 6, and the ARP protocol address + // length field is 4. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + + // If the ARP packet source IP does not match our IP address, then + // it can be ignored. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 28), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, client_addr, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // If the first four bytes of the ARP packet source hardware address + // does not equal our hardware address, then it's a conflict and should + // be passed along. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 22), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac4b, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), + // If the last two bytes of the ARP packet source hardware address + // do not equal our hardware address, then it's a conflict and should + // be passed along. + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 26), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac2b, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0x7fffffff), + // Packet announces our IP address and hardware address, so it requires + // no action. + BPF_STMT(BPF_RET + BPF_K, 0), + }; + struct sock_fprog sfp_arp = { + .len = sizeof sf_arp / sizeof sf_arp[0], + .filter = (struct sock_filter *)sf_arp, + }; + int ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, + sizeof sfp_arp) != -1; + if (ret < 0) + log_warning("%s: Failed to set BPF for defense ARP socket: %s", + client_config.interface, strerror(errno)); + return ret == 0; +} + +static int create_arp_defense_socket(uint32_t client_addr, + uint8_t client_mac[6], bool *using_bpf) +{ + assert(using_bpf); + int fd = create_arp_socket(); + *using_bpf = arp_set_bpf_defense(fd, client_addr, client_mac); + return fd; +} + +static int create_arp_basic_socket(bool *using_bpf) +{ + assert(using_bpf); + int fd = create_arp_socket(); + *using_bpf = arp_set_bpf_basic(fd); + return fd; +} + // XXX: Can share with ifch static void setup_signals_sockd(void) { @@ -427,7 +531,24 @@ static size_t execute_sockd(char *buf, size_t buflen) return 1; } case 'U': xfer_fd(create_udp_listen_socket(), 'U'); return 1; - case 'a': xfer_fd(create_arp_socket(), 'a'); return 1; + case 'a': { + bool using_bpf; + int fd = create_arp_basic_socket(&using_bpf); + xfer_fd(fd, using_bpf ? 'A' : 'a'); return 1; + } + case 'd': { + uint32_t client_addr; + uint8_t client_mac[6]; + bool using_bpf; + if (buflen < 1 + sizeof client_addr + 6) + return 0; + memcpy(&client_addr, buf + 1, sizeof client_addr); + memcpy(client_mac, buf + 1 + sizeof client_addr, 6); + int fd = create_arp_defense_socket(client_addr, client_mac, + &using_bpf); + xfer_fd(fd, using_bpf ? 'D' : 'd'); + return 11; + } case 's': xfer_fd(create_raw_broadcast_socket(), 's'); return 1; case 'u': { uint32_t client_addr;