diff options
author | Tom Gundersen <teg@jklm.no> | 2015-08-18 15:37:43 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2015-09-18 15:14:42 +0200 |
commit | 996d16975b4d802335188a3be2bbc3635c1287f3 (patch) | |
tree | 56a357205a2504f7e2995949909b960445565f1d /src | |
parent | 2b2eac39e889bdbcc6cbf44443f7682b3ac502f7 (diff) |
sd-ipv4ll: filter out unwanted ARP packets in the kernel
We currently process every ARP packet, but we should only care about the ones
relating to our IP address.
Also rename ipv4ll helpers to apr-utils.[ch], and rework the helpers a bit.
Diffstat (limited to 'src')
-rw-r--r-- | src/libsystemd-network/arp-util.c | 153 | ||||
-rw-r--r-- | src/libsystemd-network/arp-util.h (renamed from src/libsystemd-network/ipv4ll-internal.h) | 14 | ||||
-rw-r--r-- | src/libsystemd-network/ipv4ll-network.c | 91 | ||||
-rw-r--r-- | src/libsystemd-network/ipv4ll-packet.c | 71 | ||||
-rw-r--r-- | src/libsystemd-network/sd-ipv4ll.c | 80 | ||||
-rw-r--r-- | src/libsystemd-network/test-ipv4ll.c | 69 |
6 files changed, 227 insertions, 251 deletions
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c new file mode 100644 index 0000000000..2f5b9b3731 --- /dev/null +++ b/src/libsystemd-network/arp-util.c @@ -0,0 +1,153 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <linux/filter.h> +#include <arpa/inet.h> + +#include "util.h" +#include "arp-util.h" + +int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Hardware Address must be different from our own */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + }; + struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter + }; + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + _cleanup_close_ int s = -1; + int r; + + assert(ifindex > 0); + + s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + r = bind(s, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +static int arp_send_packet(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha, + bool announce) { + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htons(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + struct ether_arp arp = { + .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */ + .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */ + .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ + .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ + .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */ + }; + int r; + + assert(fd >= 0); + assert(pa != 0); + assert(ha); + + memcpy(&arp.arp_sha, ha, ETH_ALEN); + memcpy(&arp.arp_tpa, &pa, sizeof(pa)); + + if (announce) + memcpy(&arp.arp_spa, &pa, sizeof(pa)); + + r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + return 0; +} + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, false); +} + +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, true); +} diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/arp-util.h index ae0ce43985..44e5c893a7 100644 --- a/src/libsystemd-network/ipv4ll-internal.h +++ b/src/libsystemd-network/arp-util.h @@ -26,13 +26,9 @@ #include "sparse-endian.h" #include "socket-util.h" -int arp_network_bind_raw_socket(int index, union sockaddr_union *link); -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp); +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); -void arp_packet_init(struct ether_arp *arp); -void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); -int arp_packet_verify_headers(struct ether_arp *arp); - -#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); diff --git a/src/libsystemd-network/ipv4ll-network.c b/src/libsystemd-network/ipv4ll-network.c deleted file mode 100644 index 93ffed408f..0000000000 --- a/src/libsystemd-network/ipv4ll-network.c +++ /dev/null @@ -1,91 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <linux/filter.h> - -#include "util.h" -#include "ipv4ll-internal.h" - -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp) { - int r; - - assert(arp); - assert(link); - assert(fd >= 0); - - r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - return 0; -} - -int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) { - - static const struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - }; - struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter - }; - _cleanup_close_ int s = -1; - int r; - - assert(ifindex > 0); - assert(link); - - s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - link->ll.sll_family = AF_PACKET; - link->ll.sll_protocol = htons(ETH_P_ARP); - link->ll.sll_ifindex = ifindex; - link->ll.sll_halen = ETH_ALEN; - memset(link->ll.sll_addr, 0xff, ETH_ALEN); - - r = bind(s, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - r = s; - s = -1; - - return r; -} diff --git a/src/libsystemd-network/ipv4ll-packet.c b/src/libsystemd-network/ipv4ll-packet.c deleted file mode 100644 index 2b6c73ab4b..0000000000 --- a/src/libsystemd-network/ipv4ll-packet.c +++ /dev/null @@ -1,71 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ -#include <arpa/inet.h> - -#include "util.h" -#include "ipv4ll-internal.h" - -void arp_packet_init(struct ether_arp *arp) { - assert(arp); - - memzero(arp, sizeof(struct ether_arp)); - /* Header */ - arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */ - arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */ - arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */ - arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */ - arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */ -} - -void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) { - assert(ha); - - arp_packet_init(arp); - memcpy(arp->arp_sha, ha, ETH_ALEN); - memcpy(arp->arp_tpa, &pa, sizeof(pa)); -} - -void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) { - assert(ha); - - arp_packet_init(arp); - memcpy(arp->arp_sha, ha, ETH_ALEN); - memcpy(arp->arp_tpa, &pa, sizeof(pa)); - memcpy(arp->arp_spa, &pa, sizeof(pa)); -} - -int arp_packet_verify_headers(struct ether_arp *arp) { - assert(arp); - - if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) { - log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER"); - return -EINVAL; - } - if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) { - log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP"); - return -EINVAL; - } - if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) && - arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) { - log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY"); - return -EINVAL; - } - - return 0; -} diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 14b9444dab..f292a9dba9 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -28,7 +28,7 @@ #include "list.h" #include "random-util.h" -#include "ipv4ll-internal.h" +#include "arp-util.h" #include "sd-ipv4ll.h" /* Constants from the RFC */ @@ -46,6 +46,8 @@ #define IPV4LL_NETWORK 0xA9FE0000L #define IPV4LL_NETMASK 0xFFFF0000L +#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) + typedef enum IPv4LLTrigger{ IPV4LL_TRIGGER_NULL, IPV4LL_TRIGGER_PACKET, @@ -72,7 +74,6 @@ struct sd_ipv4ll { IPv4LLState state; int index; int fd; - union sockaddr_union link; int iteration; int conflict; sd_event_source *receive_message; @@ -196,8 +197,7 @@ static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { assert(ll); assert(arp); - if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0) + if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0) return true; return false; @@ -210,16 +210,13 @@ static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { if (ipv4ll_arp_conflict(ll, arp)) return true; - if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 && - memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN)) + if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0) return true; return false; } static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) { - struct ether_arp out_packet; - int out_packet_ready = 0; int r = 0; assert(ll); @@ -235,8 +232,12 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) { /* Send a probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; + r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4ll(ll, "Failed to send ARP probe."); + goto out; + } + ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0); ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); @@ -244,8 +245,12 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) { /* Send the last probe */ - arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; + r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4ll(ll, "Failed to send ARP probe."); + goto out; + } + ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1); ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); @@ -254,8 +259,12 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) { /* Send announcement packet */ - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4ll(ll, "Failed to send ARP announcement."); + goto out; + } + ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0); ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); @@ -295,8 +304,12 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void /* Defend address */ if (time_now > ll->defend_window) { ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC; - arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); - out_packet_ready = 1; + r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); + if (r < 0) { + log_ipv4ll(ll, "Failed to send ARP announcement."); + goto out; + } + } else conflicted = 1; } @@ -320,6 +333,15 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void r = ipv4ll_pick_address(ll, &ll->address); if (r < 0) goto out; + + ll->fd = safe_close(ll->fd); + + r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); + if (r < 0) + goto out; + + ll->fd = r; + ll->conflict++; ll->defend_window = 0; ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); @@ -329,15 +351,6 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); } else ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); - - } - } - - if (out_packet_ready) { - r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet); - if (r < 0) { - log_ipv4ll(ll, "failed to send arp packet out"); - goto out; } } @@ -374,10 +387,6 @@ static int ipv4ll_receive_message(sd_event_source *s, int fd, if (r < (int) sizeof(struct ether_arp)) return 0; - r = arp_packet_verify_headers(&arp); - if (r < 0) - return 0; - ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp); return 0; @@ -523,12 +532,6 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) { ll->state = IPV4LL_STATE_INIT; - r = arp_network_bind_raw_socket(ll->index, &ll->link); - - if (r < 0) - goto out; - - ll->fd = r; ll->conflict = 0; ll->defend_window = 0; ll->claimed_address = 0; @@ -551,6 +554,13 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) { goto out; } + r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); + if (r < 0) + goto out; + + safe_close(ll->fd); + ll->fd = r; + ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index d60ee98b25..e7447d322f 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -31,7 +31,7 @@ #include "event-util.h" #include "sd-ipv4ll.h" -#include "ipv4ll-internal.h" +#include "arp-util.h" static bool verbose = false; static bool extended = false; @@ -56,10 +56,10 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { } } -int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, - const struct ether_arp *arp) { +static int arp_network_send_raw_socket(int fd, int ifindex, + const struct ether_arp *arp) { assert_se(arp); - assert_se(link); + assert_se(ifindex > 0); assert_se(fd >= 0); if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) @@ -68,51 +68,35 @@ int arp_network_send_raw_socket(int fd, const union sockaddr_union *link, return 0; } -int arp_network_bind_raw_socket(int index, union sockaddr_union *link) { - if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) - return -errno; +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; - return test_fd[0]; -} + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); -static void test_arp_header(struct ether_arp *arp) { - assert_se(arp); - assert_se(arp->ea_hdr.ar_hrd == htons(ARPHRD_ETHER)); /* HTYPE */ - assert_se(arp->ea_hdr.ar_pro == htons(ETHERTYPE_IP)); /* PTYPE */ - assert_se(arp->ea_hdr.ar_hln == ETH_ALEN); /* HLEN */ - assert_se(arp->ea_hdr.ar_pln == sizeof arp->arp_spa); /* PLEN */ - assert_se(arp->ea_hdr.ar_op == htons(ARPOP_REQUEST)); /* REQUEST */ + return arp_network_send_raw_socket(fd, ifindex, &ea); } -static void test_arp_probe(void) { - struct ether_arp arp; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - be32_t pa = 0x3030; +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; - if (verbose) - printf("* %s\n", __FUNCTION__); + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); - arp_packet_probe(&arp, pa, &mac_addr); - test_arp_header(&arp); - assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0); - assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0); + return arp_network_send_raw_socket(fd, ifindex, &ea); } -static void test_arp_announce(void) { - struct ether_arp arp; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - be32_t pa = 0x3131; - - if (verbose) - printf("* %s\n", __FUNCTION__); +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) + return -errno; - arp_packet_announcement(&arp, pa, &mac_addr); - test_arp_header(&arp); - assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0); - assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0); - assert_se(memcmp(arp.arp_spa, &pa, sizeof(pa)) == 0); + return test_fd[0]; } static void test_public_api_setters(sd_event *e) { @@ -187,18 +171,15 @@ static void test_basic_request(sd_event *e) { /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); if (extended) { /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); /* PROBE */ sd_event_run(e, (uint64_t) -1); assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); - test_arp_header(&arp); sd_event_run(e, (uint64_t) -1); assert_se(basic_request_handler_bind == 1); @@ -218,8 +199,6 @@ int main(int argc, char *argv[]) { assert_se(sd_event_new(&e) >= 0); test_public_api_setters(e); - test_arp_probe(); - test_arp_announce(); test_basic_request(e); return 0; |