#include <net/if.h>
#include <arpa/inet.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "ra.h"
static bool nocarrier = false;
+static bool ptp_link = false;
static int sock = -1, rtnl = -1;
static int if_index = 0;
static struct in6_addr lladdr = IN6ADDR_ANY_INIT;
static unsigned int ra_options = 0;
static unsigned int ra_holdoff_interval = 0;
+static int ra_hoplimit = 0;
+static int ra_mtu = 0;
+static int ra_reachable = 0;
+static int ra_retransmit = 0;
struct {
struct icmp6_hdr hdr;
int ra_init(const char *ifname, const struct in6_addr *ifid,
unsigned int options, unsigned int holdoff_interval)
{
+ struct ifreq ifr;
+
ra_options = options;
ra_holdoff_interval = holdoff_interval;
if (sock < 0)
goto failure;
- if_index = if_nametoindex(ifname);
- if (!if_index)
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
goto failure;
- strncpy(if_name, ifname, sizeof(if_name) - 1);
+ ptp_link = !!(ifr.ifr_flags & IFF_POINTOPOINT);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
+ if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
+ goto failure;
+
+ if_index = ifr.ifr_ifindex;
lladdr = *ifid;
rtnl = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
return opt == end;
}
-int ra_conf_hoplimit(int newvalue)
+static bool ra_set_hoplimit(int val)
{
- static int value = 0;
-
- if (newvalue > 0)
- value = newvalue;
+ if (val > 0 && val != ra_hoplimit) {
+ ra_hoplimit = val;
+ return true;
+ }
- return value;
+ return false;
}
-int ra_conf_mtu(int newvalue)
+static bool ra_set_mtu(int val)
{
- static int value = 0;
+ if (val >= 1280 && val <= 65535 && ra_mtu != val) {
+ ra_mtu = val;
+ return true;
+ }
- if (newvalue >= 1280 && newvalue <= 65535)
- value = newvalue;
+ return false;
+}
+
+static bool ra_set_reachable(int val)
+{
+ if (val > 0 && val <= 3600000 && ra_reachable != val) {
+ ra_reachable = val;
+ return true;
+ }
- return value;
+ return false;
}
-int ra_conf_reachable(int newvalue)
+static bool ra_set_retransmit(int val)
{
- static int value = 0;
+ if (val > 0 && val <= 60000 && ra_retransmit != val) {
+ ra_retransmit = val;
+ return true;
+ }
- if (newvalue > 0 && newvalue <= 3600000)
- value = newvalue;
+ return false;
+}
- return value;
+int ra_get_hoplimit(void)
+{
+ return ra_hoplimit;
}
-int ra_conf_retransmit(int newvalue)
+int ra_get_mtu(void)
{
- static int value = 0;
+ return ra_mtu;
+}
- if (newvalue > 0 && newvalue <= 60000)
- value = newvalue;
+int ra_get_reachable(void)
+{
+ return ra_reachable;
+}
- return value;
+int ra_get_retransmit(void)
+{
+ return ra_retransmit;
}
bool ra_process(void)
.msg_controllen = sizeof(cmsg_buf),
.msg_flags = 0
};
+ struct icmpv6_opt *opt;
+ uint32_t router_valid;
+ int hlim = 0;
ssize_t len = recvmsg(sock, &msg, MSG_DONTWAIT);
if (len <= 0)
if (IN6_IS_ADDR_UNSPECIFIED(&lladdr))
continue;
- int hlim = 0;
for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL;
ch = CMSG_NXTHDR(&msg, ch))
if (ch->cmsg_level == IPPROTO_IPV6 &&
if (!ra_icmpv6_valid(&from, hlim, buf, len))
continue;
- // Stop sending solicits
- if (rs_attempt > 0) {
- alarm(0);
- rs_attempt = 0;
- }
-
if (!found) {
- odhcp6c_expire();
+ odhcp6c_expire(false);
found = true;
}
- uint32_t router_valid = ntohs(adv->nd_ra_router_lifetime);
+
+ router_valid = ntohs(adv->nd_ra_router_lifetime);
+
+ /* RFC4861 ยง6.3.7
+ * Once the host sends a Router Solicitation, and receives a valid
+ * Router Advertisement with a non-zero Router Lifetime, the host MUST
+ * desist from sending additional solicitations on that interface
+ * Moreover, a host SHOULD send at least one solicitation in the case
+ * where an advertisement is received prior to having sent a solicitation.
+ */
+ if (rs_attempt > 0 && router_valid > 0) {
+ alarm(0);
+ rs_attempt = 0;
+ }
// Parse default route
entry->target = any;
0, ra_holdoff_interval);
// Parse hoplimit
- ra_conf_hoplimit(adv->nd_ra_curhoplimit);
+ changed |= ra_set_hoplimit(adv->nd_ra_curhoplimit);
// Parse ND parameters
- ra_conf_reachable(ntohl(adv->nd_ra_reachable));
- ra_conf_retransmit(ntohl(adv->nd_ra_retransmit));
+ changed |= ra_set_reachable(ntohl(adv->nd_ra_reachable));
+ changed |= ra_set_retransmit(ntohl(adv->nd_ra_retransmit));
// Evaluate options
- struct icmpv6_opt *opt;
icmpv6_for_each_option(opt, &adv[1], &buf[len]) {
if (opt->type == ND_OPT_MTU) {
uint32_t *mtu = (uint32_t*)&opt->data[2];
- ra_conf_mtu(ntohl(*mtu));
+ changed |= ra_set_mtu(ntohl(*mtu));
} else if (opt->type == ND_OPT_ROUTE_INFORMATION && opt->len <= 3) {
+ struct icmpv6_opt_route_info *ri = (struct icmpv6_opt_route_info *)opt;
+
+ if (ri->prefix_len > 128) {
+ continue;
+ } else if (ri->prefix_len > 64) {
+ if (ri->len < 2)
+ continue;
+ } else if (ri->prefix_len > 0) {
+ if (ri->len < 1)
+ continue;
+ }
+
entry->router = from.sin6_addr;
entry->target = any;
- entry->priority = pref_to_priority(opt->data[1]);
- entry->length = opt->data[0];
- uint32_t *valid = (uint32_t*)&opt->data[2];
- entry->valid = ntohl(*valid);
- memcpy(&entry->target, &opt->data[6], (opt->len - 1) * 8);
+ entry->priority = pref_to_priority(ri->flags);
+ entry->length = ri->prefix_len;
+ entry->valid = ntohl(ri->lifetime);
+ memcpy(&entry->target, ri->prefix, (ri->len - 1) * 8);
- if (entry->length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry->target)
+ if (IN6_IS_ADDR_LINKLOCAL(&entry->target)
|| IN6_IS_ADDR_LOOPBACK(&entry->target)
|| IN6_IS_ADDR_MULTICAST(&entry->target))
continue;
|| entry->valid < entry->preferred)
continue;
- if (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
+ if ((pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
+ !ptp_link)
changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
7200, ra_holdoff_interval);
}
if (found)
- odhcp6c_expire();
+ odhcp6c_expire(false);
return found && changed;
}