odhcp6c: add -K option to set packet kernel priority
[project/odhcp6c.git] / src / dhcpv6.c
index 11cc8b6280e16f65c7a11f9f4581d7442be84a24..ca5957e3611c7d08ad0e6a6feba37ffea250dd93 100644 (file)
@@ -104,6 +104,7 @@ static int64_t t1 = 0, t2 = 0, t3 = 0;
 
 // IA states
 static enum odhcp6c_ia_mode na_mode = IA_MODE_NONE, pd_mode = IA_MODE_NONE;
+static bool stateful_only_mode = false;
 static bool accept_reconfig = false;
 // Server unicast address
 static struct in6_addr server_addr = IN6ADDR_ANY_INIT;
@@ -124,26 +125,76 @@ static uint32_t ntohl_unaligned(const uint8_t *data)
 
 static char *dhcpv6_msg_to_str(enum dhcpv6_msg msg)
 {
-       static char *dhcpv6_msg_str[] = {
-               "UNKNOWN",
-               "SOLICIT",
-               "ADVERTISE",
-               "REQUEST",
-               "RENEW",
-               "REBIND",
-               "REPLY",
-               "DECLINE",
-               "RECONFIGURE",
-               "INFORMATION REQUEST",
-       };
+       switch (msg) {
+       case DHCPV6_MSG_SOLICIT:
+               return "SOLICIT";
+
+       case DHCPV6_MSG_ADVERT:
+               return "ADVERTISE";
+
+       case DHCPV6_MSG_REQUEST:
+               return "REQUEST";
+
+       case DHCPV6_MSG_RENEW:
+               return "RENEW";
+
+       case DHCPV6_MSG_REBIND:
+               return "REBIND";
+
+       case DHCPV6_MSG_REPLY:
+               return "REPLY";
 
-       if (msg < _DHCPV6_MSG_MAX)
-               return dhcpv6_msg_str[msg];
+       case DHCPV6_MSG_RELEASE:
+               return "RELEASE";
+
+       case DHCPV6_MSG_DECLINE:
+               return "DECLINE";
+
+       case DHCPV6_MSG_RECONF:
+               return "RECONFIGURE";
+
+       case DHCPV6_MSG_INFO_REQ:
+               return "INFORMATION REQUEST";
+
+       default:
+               break;
+       }
+
+       return "UNKNOWN";
+}
+
+static char *dhcpv6_status_code_to_str(uint16_t code)
+{
+       switch (code) {
+       case DHCPV6_Success:
+               return "Success";
+
+       case DHCPV6_UnspecFail:
+               return "Unspecified Failure";
+
+       case DHCPV6_NoAddrsAvail:
+               return "No Address Available";
+
+       case DHCPV6_NoBinding:
+               return "No Binding";
+
+       case DHCPV6_NotOnLink:
+               return "Not On Link";
+
+       case DHCPV6_UseMulticast:
+               return "Use Multicast";
+
+       case DHCPV6_NoPrefixAvail:
+               return "No Prefix Available";
+
+       default:
+               break;
+       }
 
        return "Unknown";
 }
 
-int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
+int init_dhcpv6(const char *ifname, unsigned int options, int sk_prio, int sol_timeout)
 {
        client_options = options;
        dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_timeout;
@@ -238,6 +289,9 @@ int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
        if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0)
                goto failure;
 
+       if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &sk_prio, sizeof(sk_prio)) < 0)
+               goto failure;
+
        struct sockaddr_in6 client_addr = { .sin6_family = AF_INET6,
                .sin6_port = htons(DHCPV6_CLIENT_PORT), .sin6_flowinfo = 0 };
 
@@ -267,12 +321,13 @@ enum {
        IOV_TOTAL
 };
 
-int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd)
+int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd, bool stateful_only)
 {
        int mode = DHCPV6_UNKNOWN;
 
        na_mode = na;
        pd_mode = pd;
+       stateful_only_mode = stateful_only;
 
        if (na_mode == IA_MODE_NONE && pd_mode == IA_MODE_NONE)
                mode = DHCPV6_STATELESS;
@@ -660,6 +715,7 @@ int dhcpv6_request(enum dhcpv6_msg type)
                                        .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf.buf,
                                        .msg_controllen = sizeof(cmsg_buf)};
                        struct in6_pktinfo *pktinfo = NULL;
+                       const struct dhcpv6_header *hdr = (const struct dhcpv6_header *)buf;
 
                        // Check for pending signal
                        if (odhcp6c_signal_process())
@@ -703,8 +759,8 @@ int dhcpv6_request(enum dhcpv6_msg type)
 
                        round_start = odhcp6c_get_milli_time();
                        elapsed = round_start - start;
-                       syslog(LOG_NOTICE, "Got a valid reply after %"PRIu64"ms",
-                                       elapsed);
+                       syslog(LOG_NOTICE, "Got a valid %s after %"PRIu64"ms",
+                              dhcpv6_msg_to_str(hdr->msg_type), elapsed);
 
                        if (retx->handler_reply)
                                len = retx->handler_reply(type, rc, opt, opt_end, &addr);
@@ -829,8 +885,21 @@ int dhcpv6_poll_reconfigure(void)
 {
        int ret = dhcpv6_request(DHCPV6_MSG_UNKNOWN);
 
-       if (ret != -1)
+       switch (ret) {
+       /*
+        * Only RENEW/REBIND/INFORMATION REQUEST
+        * message transmission can be requested
+        * by a RECONFIGURE
+        */
+       case DHCPV6_MSG_RENEW:
+       case DHCPV6_MSG_REBIND:
+       case DHCPV6_MSG_INFO_REQ:
                ret = dhcpv6_request(ret);
+               break;
+
+       default:
+               break;
+       }
 
        return ret;
 }
@@ -855,9 +924,8 @@ static int dhcpv6_handle_reconfigure(enum dhcpv6_msg orig, const int rc,
                        // Fall through
                        case DHCPV6_MSG_INFO_REQ:
                                msg = odata[0];
-                               syslog(LOG_NOTICE, "Got a %s (msg-type %s)",
-                                               dhcpv6_msg_to_str(otype),
-                                               dhcpv6_msg_to_str(msg));
+                               syslog(LOG_NOTICE, "Need to respond with %s in reply to %s",
+                                      dhcpv6_msg_to_str(msg), dhcpv6_msg_to_str(DHCPV6_MSG_RECONF));
                                break;
 
                        default:
@@ -869,7 +937,7 @@ static int dhcpv6_handle_reconfigure(enum dhcpv6_msg orig, const int rc,
        if (msg != DHCPV6_MSG_UNKNOWN)
                dhcpv6_handle_reply(orig, rc, NULL, NULL, NULL);
 
-       return (msg == DHCPV6_MSG_UNKNOWN? -1: 1);
+       return (msg == DHCPV6_MSG_UNKNOWN? -1: (int)msg);
 }
 
 // Collect all advertised servers
@@ -941,7 +1009,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
                }
        }
 
-       if ((!have_na && na_mode == IA_MODE_FORCE) ||
+       if ((stateful_only_mode && !have_na && !have_pd) ||
+                       (!have_na && na_mode == IA_MODE_FORCE) ||
                        (!have_pd && pd_mode == IA_MODE_FORCE)) {
                /*
                 * RFC7083 states to process the SOL_MAX_RT and
@@ -1000,7 +1069,7 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
        unsigned int updated_IAs = 0;
        bool handled_status_codes[_DHCPV6_Status_Max] = { false, };
 
-       odhcp6c_expire();
+       odhcp6c_expire(true);
 
        if (orig == DHCPV6_MSG_UNKNOWN) {
                static time_t last_update = 0;
@@ -1280,10 +1349,10 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end)
        t1 = ntohl(ia_hdr->t1);
        t2 = ntohl(ia_hdr->t2);
 
-       if (t1 > t2)
+       if (t1 > t2 && t1 > 0 && t2 > 0)
                return 0;
 
-       syslog(LOG_INFO, "IAID %04x T1 %d T2 %d", htonl(ia_hdr->iaid), t1, t2);
+       syslog(LOG_INFO, "%s %04x T1 %d T2 %d", ntohs(ia_hdr->type) == DHCPV6_OPT_IA_PD ? "IA_PD" : "IA_NA", ntohl(ia_hdr->iaid), t1, t2);
 
        // Update address IA
        dhcpv6_for_each_option(&ia_hdr[1], end, otype, olen, odata) {
@@ -1395,12 +1464,19 @@ static unsigned int dhcpv6_calc_refresh_timers(void)
 {
        struct odhcp6c_entry *e;
        size_t ia_na_entries, ia_pd_entries, i;
+       size_t invalid_entries = 0;
        int64_t l_t1 = UINT32_MAX, l_t2 = UINT32_MAX, l_t3 = 0;
 
        e = odhcp6c_get_state(STATE_IA_NA, &ia_na_entries);
        ia_na_entries /= sizeof(*e);
 
        for (i = 0; i < ia_na_entries; i++) {
+               /* Exclude invalid IA_NA entries */
+               if (!e[i].valid) {
+                       invalid_entries++;
+                       continue;
+               }
+
                if (e[i].t1 < l_t1)
                        l_t1 = e[i].t1;
 
@@ -1415,6 +1491,12 @@ static unsigned int dhcpv6_calc_refresh_timers(void)
        ia_pd_entries /= sizeof(*e);
 
        for (i = 0; i < ia_pd_entries; i++) {
+               /* Exclude invalid IA_PD entries */
+               if (!e[i].valid) {
+                       invalid_entries++;
+                       continue;
+               }
+
                if (e[i].t1 < l_t1)
                        l_t1 = e[i].t1;
 
@@ -1425,7 +1507,7 @@ static unsigned int dhcpv6_calc_refresh_timers(void)
                        l_t3 = e[i].valid;
        }
 
-       if (ia_pd_entries || ia_na_entries) {
+       if (ia_pd_entries + ia_na_entries - invalid_entries) {
                t1 = l_t1;
                t2 = l_t2;
                t3 = l_t3;
@@ -1455,8 +1537,8 @@ static void dhcpv6_log_status_code(const uint16_t code, const char *scope,
 
        *dst = 0;
 
-       syslog(LOG_WARNING, "Server returned %s status %i %s",
-               scope, code, buf);
+       syslog(LOG_WARNING, "Server returned %s status '%s %s'",
+               scope, dhcpv6_status_code_to_str(code), buf);
 }
 
 static void dhcpv6_handle_status_code(const enum dhcpv6_msg orig,