router: don't announce prefixes with valid lifetime equal to 0
[project/odhcpd.git] / src / ndp.c
index 8824879..5866588 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
 struct event_socket {
        struct odhcpd_event ev;
        struct nl_sock *sock;
+       int sock_bufsize;
 };
 
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, void *dest);
 static void handle_rtnl_event(struct odhcpd_event *ev);
 static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
-static void catch_rtnetlink(int error);
+static void catch_rtnl_err(struct odhcpd_event *e, int error);
 
 static int ping_socket = -1;
 static struct event_socket rtnl_event = {
        .ev = {
                .uloop = {.fd = - 1, },
                .handle_dgram = NULL,
-               .handle_error = catch_rtnetlink,
+               .handle_error = catch_rtnl_err,
                .recv_msgs = handle_rtnl_event,
        },
        .sock = NULL,
+       .sock_bufsize = 133120,
 };
 
 // Filter ICMPv6 messages of type neighbor soliciation
@@ -75,7 +77,7 @@ static const struct sock_fprog bpf_prog = {sizeof(bpf) / sizeof(*bpf), bpf};
 // Initialize NDP-proxy
 int init_ndp(void)
 {
-       int val = 256 * 1024;
+       int val = 2;
 
        rtnl_event.sock = odhcpd_create_nl_socket(NETLINK_ROUTE);
        if (!rtnl_event.sock)
@@ -83,7 +85,7 @@ int init_ndp(void)
 
        rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock);
 
-       if (nl_socket_set_buffer_size(rtnl_event.sock, val, 0))
+       if (nl_socket_set_buffer_size(rtnl_event.sock, rtnl_event.sock_bufsize, 0))
                goto err;
 
        nl_socket_disable_seq_check(rtnl_event.sock);
@@ -105,7 +107,6 @@ int init_ndp(void)
                        return -1;
        }
 
-       val = 2;
        setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
 
        // This is required by RFC 4861
@@ -169,10 +170,17 @@ static void dump_addr_table(void)
 
 int setup_ndp_interface(struct interface *iface, bool enable)
 {
+       int ret = 0, procfd;
+       bool dump_neigh = false;
        char procbuf[64];
+
        snprintf(procbuf, sizeof(procbuf), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface->ifname);
-       int procfd = open(procbuf, O_WRONLY);
-       bool dump_neigh = false;
+       procfd = open(procbuf, O_WRONLY);
+
+       if (procfd < 0) {
+               ret = -1;
+               goto out;
+       }
 
        if (iface->ndp_event.uloop.fd > 0) {
                uloop_fd_delete(&iface->ndp_event.uloop);
@@ -191,13 +199,13 @@ int setup_ndp_interface(struct interface *iface, bool enable)
 
        if (enable && iface->ndp == RELAYD_RELAY) {
                if (write(procfd, "1\n", 2) < 0) {}
-               close(procfd);
 
                int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
                if (sock < 0) {
                        syslog(LOG_ERR, "Unable to open packet socket: %s",
                                        strerror(errno));
-                       return -1;
+                       ret = -1;
+                       goto out;
                }
 
 #ifdef PACKET_RECV_TYPE
@@ -208,7 +216,8 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
                                &bpf_prog, sizeof(bpf_prog))) {
                        syslog(LOG_ERR, "Failed to set BPF: %s", strerror(errno));
-                       return -1;
+                       ret = -1;
+                       goto out;
                }
 
                struct sockaddr_ll ll = {
@@ -234,13 +243,16 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                        dump_neigh_table(false);
                else
                        dump_neigh = false;
-       } else
-               close(procfd);
+       }
 
        if (dump_neigh)
                dump_neigh_table(true);
 
-       return 0;
+out:
+       if (procfd >= 0)
+               close(procfd);
+
+       return ret;
 }
 
 
@@ -316,8 +328,8 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add
 static int prefixcmp(const void *va, const void *vb)
 {
        const struct odhcpd_ipaddr *a = va, *b = vb;
-       uint32_t a_pref = ((a->addr.s6_addr[0] & 0xfe) != 0xfc) ? a->preferred : 1;
-       uint32_t b_pref = ((b->addr.s6_addr[0] & 0xfe) != 0xfc) ? b->preferred : 1;
+       uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr) ? 1 : a->preferred;
+       uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr) ? 1 : b->preferred;
        return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
 }
 
@@ -522,8 +534,22 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
        return NL_OK;
 }
 
-static void catch_rtnetlink(int error)
+static void catch_rtnl_err(struct odhcpd_event *e, int error)
 {
-       if (error == ENOBUFS)
-               dump_addr_table();
+       struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
+
+       if (error != ENOBUFS)
+               goto err;
+
+       /* Double netlink event buffer size */
+       ev_sock->sock_bufsize *= 2;
+
+       if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0))
+               goto err;
+
+       dump_addr_table();
+       return;
+
+err:
+       odhcpd_deregister(e);
 }