sd-ipv4ll: filter out unwanted ARP packets in the kernel
authorTom Gundersen <teg@jklm.no>
Tue, 18 Aug 2015 13:37:43 +0000 (15:37 +0200)
committerTom Gundersen <teg@jklm.no>
Fri, 18 Sep 2015 13:14:42 +0000 (15:14 +0200)
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.

Makefile.am
src/libsystemd-network/arp-util.c [new file with mode: 0644]
src/libsystemd-network/arp-util.h [moved from src/libsystemd-network/ipv4ll-internal.h with 59% similarity]
src/libsystemd-network/ipv4ll-network.c [deleted file]
src/libsystemd-network/ipv4ll-packet.c [deleted file]
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/test-ipv4ll.c

index c395840..3930647 100644 (file)
@@ -3214,9 +3214,8 @@ libsystemd_network_la_SOURCES = \
        src/libsystemd-network/dhcp-lease-internal.h \
        src/libsystemd-network/sd-dhcp-lease.c \
        src/libsystemd-network/sd-ipv4ll.c \
-       src/libsystemd-network/ipv4ll-network.c \
-       src/libsystemd-network/ipv4ll-packet.c \
-       src/libsystemd-network/ipv4ll-internal.h \
+       src/libsystemd-network/arp-util.h \
+       src/libsystemd-network/arp-util.c \
        src/libsystemd-network/sd-pppoe.c \
        src/libsystemd-network/network-internal.c \
        src/libsystemd-network/network-internal.h \
@@ -3273,7 +3272,7 @@ test_dhcp_server_LDADD = \
 
 test_ipv4ll_SOURCES = \
        src/systemd/sd-ipv4ll.h \
-       src/libsystemd-network/ipv4ll-internal.h \
+       src/libsystemd-network/arp-util.h \
        src/libsystemd-network/test-ipv4ll.c
 
 test_ipv4ll_LDADD = \
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
new file mode 100644 (file)
index 0000000..2f5b9b3
--- /dev/null
@@ -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);
+}
similarity index 59%
rename from src/libsystemd-network/ipv4ll-internal.h
rename to src/libsystemd-network/arp-util.h
index ae0ce43..44e5c89 100644 (file)
 #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 (file)
index 93ffed4..0000000
+++ /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 (file)
index 2b6c73a..0000000
+++ /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;
-}
index 14b9444..f292a9d 100644 (file)
@@ -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,
index d60ee98..e7447d3 100644 (file)
@@ -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;