]> git.decadent.org.uk Git - odhcp6c.git/blobdiff - src/ra.c
Basic error checking when initializing ra
[odhcp6c.git] / src / ra.c
index f41602a587e00b87d10299bbac62df785b873f11..64cc8d85ac7ab961382ac80f7aaf6c4557fa4030 100644 (file)
--- a/src/ra.c
+++ b/src/ra.c
 
 #include <linux/rtnetlink.h>
 
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#ifndef NETLINK_ADD_MEMBERSHIP
+#define NETLINK_ADD_MEMBERSHIP 1
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000
+#endif
 
 #include "odhcp6c.h"
 #include "ra.h"
 
 
-static int sock = -1;
-static unsigned if_index = 0;
+static bool nocarrier = false;
+
+static int sock = -1, rtnl = -1;
+static int if_index = 0;
 static char if_name[IF_NAMESIZE] = {0};
 static volatile int rs_attempt = 0;
 static struct in6_addr lladdr = IN6ADDR_ANY_INIT;
@@ -44,11 +57,40 @@ static void ra_send_rs(int signal __attribute__((unused)));
 
 int ra_init(const char *ifname, const struct in6_addr *ifid)
 {
+       const pid_t ourpid = getpid();
        sock = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+       if (sock < 0)
+               return -1;
+
        if_index = if_nametoindex(ifname);
+       if (!if_index)
+               return -1;
+
        strncpy(if_name, ifname, sizeof(if_name) - 1);
        lladdr = *ifid;
 
+       rtnl = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (rtnl < 0)
+               return -1;
+
+       struct sockaddr_nl rtnl_kernel = { .nl_family = AF_NETLINK };
+       if (connect(rtnl, (const struct sockaddr*)&rtnl_kernel, sizeof(rtnl_kernel)) < 0)
+               return -1;
+
+       int val = RTNLGRP_LINK;
+       setsockopt(rtnl, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &val, sizeof(val));
+       fcntl(rtnl, F_SETOWN, ourpid);
+       fcntl(rtnl, F_SETFL, fcntl(sock, F_GETFL) | O_ASYNC);
+
+       struct {
+               struct nlmsghdr hdr;
+               struct ifinfomsg ifi;
+       } req = {
+               .hdr = {sizeof(req), RTM_GETLINK, NLM_F_REQUEST, 1, 0},
+               .ifi = {.ifi_index = if_index}
+       };
+       send(rtnl, &req, sizeof(req), 0);
+
        // Filter ICMPv6 package types
        struct icmp6_filter filt;
        ICMP6_FILTER_SETBLOCKALL(&filt);
@@ -60,7 +102,7 @@ int ra_init(const char *ifname, const struct in6_addr *ifid)
        setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &an, sizeof(an));
 
        // Let the kernel compute our checksums
-       int val = 2;
+       val = 2;
        setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
 
        // This is required by RFC 4861
@@ -75,7 +117,6 @@ int ra_init(const char *ifname, const struct in6_addr *ifid)
        setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
 
        // Add async-mode
-       const pid_t ourpid = getpid();
        fcntl(sock, F_SETOWN, ourpid);
        fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_ASYNC);
 
@@ -117,13 +158,50 @@ static void update_proc(const char *sect, const char *opt, uint32_t value)
 }
 
 
+bool ra_link_up(void)
+{
+       static bool firstcall = true;
+       struct {
+               struct nlmsghdr hdr;
+               struct ifinfomsg msg;
+               uint8_t pad[4000];
+       } resp;
+
+       bool ret = false;
+       ssize_t read;
+
+       do {
+               read = recv(rtnl, &resp, sizeof(resp), MSG_DONTWAIT);
+               if (read < 0 || !NLMSG_OK(&resp.hdr, (size_t)read) ||
+                               resp.hdr.nlmsg_type != RTM_NEWLINK ||
+                               resp.msg.ifi_index != if_index)
+                       continue;
+
+               bool hascarrier = resp.msg.ifi_flags & IFF_LOWER_UP;
+               if (!firstcall && nocarrier != !hascarrier)
+                       ret = true;
+
+               nocarrier = !hascarrier;
+               firstcall = false;
+       } while (read > 0);
+
+       if (ret) {
+               syslog(LOG_NOTICE, "carrier => %i event on %s", (int)!nocarrier, if_name);
+
+               rs_attempt = 0;
+               ra_send_rs(SIGALRM);
+       }
+
+       return ret;
+}
+
 bool ra_process(void)
 {
        bool found = false;
        bool changed = false;
        uint8_t buf[1500], cmsg_buf[128];
        struct nd_router_advert *adv = (struct nd_router_advert*)buf;
-       struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, IN6ADDR_ANY_INIT, 0, 0, 0};
+       struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0};
        const struct in6_addr any = IN6ADDR_ANY_INIT;
 
        if (IN6_IS_ADDR_UNSPECIFIED(&lladdr)) {