• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/odhcp6c/src/ra.c

  1 /**
  2  * Copyright (C) 2012-2014 Steven Barth <steven@midlink.org>
  3  * Copyright (C) 2017-2018 Hans Dedecker <dedeckeh@gmail.com>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License v2 as published by
  7  * the Free Software Foundation.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  *
 14  */
 15 
 16 #include <errno.h>
 17 #include <fcntl.h>
 18 #include <stdio.h>
 19 #include <signal.h>
 20 #include <string.h>
 21 #include <stddef.h>
 22 #include <stdbool.h>
 23 #include <syslog.h>
 24 #include <unistd.h>
 25 #include <resolv.h>
 26 #include <alloca.h>
 27 
 28 #include <net/if.h>
 29 #include <arpa/inet.h>
 30 #include <sys/ioctl.h>
 31 #include <sys/socket.h>
 32 #include <sys/types.h>
 33 #include <netinet/in.h>
 34 #include <netinet/icmp6.h>
 35 
 36 #include <linux/rtnetlink.h>
 37 
 38 #ifndef SOL_NETLINK
 39 #define SOL_NETLINK 270
 40 #endif
 41 
 42 #ifndef NETLINK_ADD_MEMBERSHIP
 43 #define NETLINK_ADD_MEMBERSHIP 1
 44 #endif
 45 
 46 #ifndef IFF_LOWER_UP
 47 #define IFF_LOWER_UP 0x10000
 48 #endif
 49 
 50 #include "odhcp6c.h"
 51 #include "ra.h"
 52 
 53 static bool nocarrier = false;
 54 static bool ptp_link = false;
 55 
 56 static int sock = -1, rtnl = -1;
 57 static int if_index = 0;
 58 static char if_name[IF_NAMESIZE] = {0};
 59 static volatile int rs_attempt = 0;
 60 static struct in6_addr lladdr = IN6ADDR_ANY_INIT;
 61 static unsigned int ra_options = 0;
 62 static unsigned int ra_holdoff_interval = 0;
 63 static int ra_hoplimit = 0;
 64 static int ra_mtu = 0;
 65 static int ra_reachable = 0;
 66 static int ra_retransmit = 0;
 67 
 68 struct {
 69         struct icmp6_hdr hdr;
 70         struct icmpv6_opt lladdr;
 71 } rs = {
 72         .hdr = {ND_ROUTER_SOLICIT, 0, 0, {{0}}},
 73         .lladdr = {ND_OPT_SOURCE_LINKADDR, 1, {0}},
 74 };
 75 
 76 static void ra_send_rs(int signal __attribute__((unused)));
 77 
 78 int ra_init(const char *ifname, const struct in6_addr *ifid,
 79                 unsigned int options, unsigned int holdoff_interval)
 80 {
 81         struct ifreq ifr;
 82 
 83         ra_options = options;
 84         ra_holdoff_interval = holdoff_interval;
 85 
 86         const pid_t ourpid = getpid();
 87         sock = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
 88         if (sock < 0)
 89                 goto failure;
 90 
 91         memset(&ifr, 0, sizeof(ifr));
 92         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
 93         if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
 94                 goto failure;
 95 
 96         ptp_link = !!(ifr.ifr_flags & IFF_POINTOPOINT);
 97 
 98         memset(&ifr, 0, sizeof(ifr));
 99         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
100         if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
101                 goto failure;
102 
103         if_index = ifr.ifr_ifindex;
104         lladdr = *ifid;
105 
106         rtnl = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
107         if (rtnl < 0)
108                 goto failure;
109 
110         struct sockaddr_nl rtnl_kernel = { .nl_family = AF_NETLINK };
111         if (connect(rtnl, (const struct sockaddr*)&rtnl_kernel, sizeof(rtnl_kernel)) < 0)
112                 goto failure;
113 
114         int val = RTNLGRP_LINK;
115         if (setsockopt(rtnl, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &val, sizeof(val)) < 0)
116                 goto failure;
117 
118         if (fcntl(rtnl, F_SETOWN, ourpid) < 0)
119                 goto failure;
120 
121         if (fcntl(rtnl, F_SETFL, fcntl(sock, F_GETFL) | O_ASYNC) < 0)
122                 goto failure;
123 
124         struct {
125                 struct nlmsghdr hdr;
126                 struct ifinfomsg ifi;
127         } req = {
128                 .hdr = {sizeof(req), RTM_GETLINK, NLM_F_REQUEST, 1, 0},
129                 .ifi = {.ifi_index = if_index}
130         };
131         if (send(rtnl, &req, sizeof(req), 0) < 0)
132                 goto failure;
133 
134         ra_link_up();
135 
136         // Filter ICMPv6 package types
137         struct icmp6_filter filt;
138         ICMP6_FILTER_SETBLOCKALL(&filt);
139         ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
140         if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) < 0)
141                 goto failure;
142 
143         // Bind to all-nodes
144         struct ipv6_mreq an = {ALL_IPV6_NODES, if_index};
145         if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &an, sizeof(an)) < 0)
146                 goto failure;
147 
148         // Let the kernel compute our checksums
149         val = 2;
150         if (setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)) < 0)
151                 goto failure;
152 
153         // This is required by RFC 4861
154         val = 255;
155         if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) < 0)
156                 goto failure;
157 
158         // Receive multicast hops
159         val = 1;
160         if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)) < 0)
161                 goto failure;
162 
163         // Bind to one device
164         if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0)
165                 goto failure;
166 
167         // Add async-mode
168         if (fcntl(sock, F_SETOWN, ourpid) < 0)
169                 goto failure;
170 
171         val = fcntl(sock, F_GETFL);
172         if (val < 0)
173                 goto failure;
174 
175         if (fcntl(sock, F_SETFL, val | O_ASYNC) < 0)
176                 goto failure;
177 
178         // Send RS
179         signal(SIGALRM, ra_send_rs);
180         ra_send_rs(SIGALRM);
181 
182         return 0;
183 
184 failure:
185         if (sock >= 0)
186                 close(sock);
187 
188         if (rtnl >= 0)
189                 close(rtnl);
190 
191         return -1;
192 }
193 
194 static void ra_send_rs(int signal __attribute__((unused)))
195 {
196         const struct sockaddr_in6 dest = {AF_INET6, 0, 0, ALL_IPV6_ROUTERS, if_index};
197         const struct icmpv6_opt llnull = {ND_OPT_SOURCE_LINKADDR, 1, {0}};
198         size_t len;
199 
200         if ((rs_attempt % 2 == 0) && memcmp(&rs.lladdr, &llnull, sizeof(llnull)))
201                 len = sizeof(rs);
202         else
203                 len = sizeof(struct icmp6_hdr);
204 
205         if (sendto(sock, &rs, len, MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)) < 0)
206                 syslog(LOG_ERR, "Failed to send RS (%s)",  strerror(errno));
207 
208         if (++rs_attempt <= 3)
209                 alarm(4);
210 }
211 
212 static int16_t pref_to_priority(uint8_t flags)
213 {
214         flags = (flags >> 3) & 0x03;
215 
216         return (flags == 0x0) ? 512 : (flags == 0x1) ? 384 :
217                         (flags == 0x3) ? 640 : -1;
218 }
219 
220 bool ra_link_up(void)
221 {
222         static bool firstcall = true;
223         struct {
224                 struct nlmsghdr hdr;
225                 struct ifinfomsg msg;
226                 uint8_t pad[4000];
227         } resp;
228         bool ret = false;
229         ssize_t read;
230 
231         do {
232                 read = recv(rtnl, &resp, sizeof(resp), MSG_DONTWAIT);
233 
234                 if (read < 0 || !NLMSG_OK(&resp.hdr, (size_t)read) ||
235                                 resp.hdr.nlmsg_type != RTM_NEWLINK ||
236                                 resp.msg.ifi_index != if_index)
237                         continue;
238 
239                 ssize_t alen = NLMSG_PAYLOAD(&resp.hdr, sizeof(resp.msg));
240                 for (struct rtattr *rta = (struct rtattr*)(resp.pad);
241                                 RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen)) {
242                         if (rta->rta_type == IFLA_ADDRESS &&
243                                         RTA_PAYLOAD(rta) >= sizeof(rs.lladdr.data))
244                                 memcpy(rs.lladdr.data, RTA_DATA(rta), sizeof(rs.lladdr.data));
245                 }
246 
247                 bool hascarrier = resp.msg.ifi_flags & IFF_LOWER_UP;
248                 if (!firstcall && nocarrier != !hascarrier)
249                         ret = true;
250 
251                 nocarrier = !hascarrier;
252                 firstcall = false;
253         } while (read > 0);
254 
255         if (ret) {
256                 syslog(LOG_NOTICE, "carrier => %i event on %s", (int)!nocarrier, if_name);
257 
258                 rs_attempt = 0;
259                 ra_send_rs(SIGALRM);
260         }
261 
262         return ret;
263 }
264 
265 static bool ra_icmpv6_valid(struct sockaddr_in6 *source, int hlim, uint8_t *data, size_t len)
266 {
267         struct icmp6_hdr *hdr = (struct icmp6_hdr*)data;
268         struct icmpv6_opt *opt, *end = (struct icmpv6_opt*)&data[len];
269 
270         if (hlim != 255 || len < sizeof(*hdr) || hdr->icmp6_code)
271                 return false;
272 
273         switch (hdr->icmp6_type) {
274         case ND_ROUTER_ADVERT:
275                 if (!IN6_IS_ADDR_LINKLOCAL(&source->sin6_addr))
276                         return false;
277 
278                 opt = (struct icmpv6_opt*)((struct nd_router_advert*)data + 1);
279                 break;
280 
281         default:
282                 return false;
283         }
284 
285         icmpv6_for_each_option(opt, opt, end)
286                 ;
287 
288         return opt == end;
289 }
290 
291 static bool ra_set_hoplimit(int val)
292 {
293         if (val > 0 && val != ra_hoplimit) {
294                 ra_hoplimit = val;
295                 return true;
296         }
297 
298         return false;
299 }
300 
301 static bool ra_set_mtu(int val)
302 {
303         if (val >= 1280 && val <= 65535 && ra_mtu != val) {
304                 ra_mtu = val;
305                 return true;
306         }
307 
308         return false;
309 }
310 
311 static bool ra_set_reachable(int val)
312 {
313         if (val > 0 && val <= 3600000 && ra_reachable != val) {
314                 ra_reachable = val;
315                 return true;
316         }
317 
318         return false;
319 }
320 
321 static bool ra_set_retransmit(int val)
322 {
323         if (val > 0 && val <= 60000 && ra_retransmit != val) {
324                 ra_retransmit = val;
325                 return true;
326         }
327 
328         return false;
329 }
330 
331 int ra_get_hoplimit(void)
332 {
333         return ra_hoplimit;
334 }
335 
336 int ra_get_mtu(void)
337 {
338         return ra_mtu;
339 }
340 
341 int ra_get_reachable(void)
342 {
343         return ra_reachable;
344 }
345 
346 int ra_get_retransmit(void)
347 {
348         return ra_retransmit;
349 }
350 
351 bool ra_process(void)
352 {
353         bool found = false;
354         bool changed = false;
355         uint8_t buf[1500] _aligned(4);
356         union {
357                 struct cmsghdr hdr;
358                 uint8_t buf[CMSG_SPACE(sizeof(int))];
359         } cmsg_buf;
360         struct nd_router_advert *adv = (struct nd_router_advert*)buf;
361         struct odhcp6c_entry *entry = alloca(sizeof(*entry) + 256);
362         const struct in6_addr any = IN6ADDR_ANY_INIT;
363 
364         memset(entry, 0, sizeof(*entry));
365 
366         if (IN6_IS_ADDR_UNSPECIFIED(&lladdr)) {
367                 struct sockaddr_in6 addr = {AF_INET6, 0, 0, ALL_IPV6_ROUTERS, if_index};
368                 socklen_t alen = sizeof(addr);
369                 int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
370 
371                 if (sock >= 0) {
372                         if (!connect(sock, (struct sockaddr*)&addr, sizeof(addr)) &&
373                                         !getsockname(sock, (struct sockaddr*)&addr, &alen))
374                                 lladdr = addr.sin6_addr;
375 
376                         close(sock);
377                 }
378         }
379 
380         while (true) {
381                 struct sockaddr_in6 from;
382                 struct iovec iov = {buf, sizeof(buf)};
383                 struct msghdr msg = {
384                         .msg_name = (void *) &from,
385                         .msg_namelen = sizeof(from),
386                         .msg_iov = &iov,
387                         .msg_iovlen = 1,
388                         .msg_control = cmsg_buf.buf,
389                         .msg_controllen = sizeof(cmsg_buf),
390                         .msg_flags = 0
391                 };
392                 struct icmpv6_opt *opt;
393                 uint32_t router_valid;
394                 int hlim = 0;
395 
396                 ssize_t len = recvmsg(sock, &msg, MSG_DONTWAIT);
397                 if (len <= 0)
398                         break;
399 
400                 if (IN6_IS_ADDR_UNSPECIFIED(&lladdr))
401                         continue;
402 
403                 for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL;
404                                 ch = CMSG_NXTHDR(&msg, ch))
405                         if (ch->cmsg_level == IPPROTO_IPV6 &&
406                                         ch->cmsg_type == IPV6_HOPLIMIT)
407                                 memcpy(&hlim, CMSG_DATA(ch), sizeof(hlim));
408 
409                 if (!ra_icmpv6_valid(&from, hlim, buf, len))
410                         continue;
411 
412                 if (!found) {
413                         odhcp6c_expire(false);
414                         found = true;
415                 }
416 
417                 router_valid = ntohs(adv->nd_ra_router_lifetime);
418 
419                 /* RFC4861 ยง6.3.7
420                  * Once the host sends a Router Solicitation, and receives a valid
421                  * Router Advertisement with a non-zero Router Lifetime, the host MUST
422                  * desist from sending additional solicitations on that interface
423                  * Moreover, a host SHOULD send at least one solicitation in the case
424                  * where an advertisement is received prior to having sent a solicitation.
425                  */
426                 if (rs_attempt > 0 && router_valid > 0) {
427                         alarm(0);
428                         rs_attempt = 0;
429                 }
430 
431                 // Parse default route
432                 entry->target = any;
433                 entry->length = 0;
434                 entry->router = from.sin6_addr;
435                 entry->priority = pref_to_priority(adv->nd_ra_flags_reserved);
436                 if (entry->priority < 0)
437                         entry->priority = pref_to_priority(0);
438 
439                 entry->valid = router_valid;
440                 entry->preferred = entry->valid;
441                 changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
442                                                 0, ra_holdoff_interval);
443 
444                 // Parse hoplimit
445                 changed |= ra_set_hoplimit(adv->nd_ra_curhoplimit);
446 
447                 // Parse ND parameters
448                 changed |= ra_set_reachable(ntohl(adv->nd_ra_reachable));
449                 changed |= ra_set_retransmit(ntohl(adv->nd_ra_retransmit));
450 
451                 // Evaluate options
452                 icmpv6_for_each_option(opt, &adv[1], &buf[len]) {
453                         if (opt->type == ND_OPT_MTU) {
454                                 uint32_t *mtu = (uint32_t*)&opt->data[2];
455                                 changed |= ra_set_mtu(ntohl(*mtu));
456                         } else if (opt->type == ND_OPT_ROUTE_INFORMATION && opt->len <= 3) {
457                                 struct icmpv6_opt_route_info *ri = (struct icmpv6_opt_route_info *)opt;
458 
459                                 if (ri->prefix_len > 128) {
460                                         continue;
461                                 } else if (ri->prefix_len > 64) {
462                                         if (ri->len < 2)
463                                                 continue;
464                                 } else if (ri->prefix_len > 0) {
465                                         if (ri->len < 1)
466                                                 continue;
467                                 }
468 
469                                 entry->router = from.sin6_addr;
470                                 entry->target = any;
471                                 entry->priority = pref_to_priority(ri->flags);
472                                 entry->length = ri->prefix_len;
473                                 entry->valid = ntohl(ri->lifetime);
474                                 memcpy(&entry->target, ri->prefix, (ri->len - 1) * 8);
475 
476                                 if (IN6_IS_ADDR_LINKLOCAL(&entry->target)
477                                                 || IN6_IS_ADDR_LOOPBACK(&entry->target)
478                                                 || IN6_IS_ADDR_MULTICAST(&entry->target))
479                                         continue;
480 
481                                 if (entry->priority > 0)
482                                         changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
483                                                                         0, ra_holdoff_interval);
484                         } else if (opt->type == ND_OPT_PREFIX_INFORMATION && opt->len == 4) {
485                                 struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info*)opt;
486                                 entry->router = any;
487                                 entry->target = pinfo->nd_opt_pi_prefix;
488                                 entry->priority = 256;
489                                 entry->length = pinfo->nd_opt_pi_prefix_len;
490                                 entry->valid = ntohl(pinfo->nd_opt_pi_valid_time);
491                                 entry->preferred = ntohl(pinfo->nd_opt_pi_preferred_time);
492 
493                                 if (entry->length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry->target)
494                                                 || IN6_IS_ADDR_LOOPBACK(&entry->target)
495                                                 || IN6_IS_ADDR_MULTICAST(&entry->target)
496                                                 || entry->valid < entry->preferred)
497                                         continue;
498 
499                                 if ((pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
500                                     !ptp_link)
501                                         changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
502                                                                         7200, ra_holdoff_interval);
503 
504                                 if (!(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ||
505                                                 pinfo->nd_opt_pi_prefix_len != 64)
506                                         continue;
507 
508                                 entry->target.s6_addr32[2] = lladdr.s6_addr32[2];
509                                 entry->target.s6_addr32[3] = lladdr.s6_addr32[3];
510 
511                                 changed |= odhcp6c_update_entry(STATE_RA_PREFIX, entry,
512                                                                 7200, ra_holdoff_interval);
513                         } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 2) {
514                                 entry->router = from.sin6_addr;
515                                 entry->priority = 0;
516                                 entry->length = 128;
517                                 uint32_t *valid = (uint32_t*)&opt->data[2];
518                                 entry->valid = ntohl(*valid);
519                                 entry->preferred = 0;
520 
521                                 for (ssize_t i = 0; i < (opt->len - 1) / 2; ++i) {
522                                         memcpy(&entry->target, &opt->data[6 + i * sizeof(entry->target)],
523                                                         sizeof(entry->target));
524                                         changed |= odhcp6c_update_entry(STATE_RA_DNS, entry,
525                                                                         0, ra_holdoff_interval);
526                                 }
527                         } else if (opt->type == ND_OPT_DNSSL && opt->len > 1) {
528                                 uint32_t *valid = (uint32_t*)&opt->data[2];
529                                 uint8_t *buf = &opt->data[6];
530                                 uint8_t *end = &buf[(opt->len - 1) * 8];
531 
532                                 entry->router = from.sin6_addr;
533                                 entry->valid = ntohl(*valid);
534 
535                                 while (buf < end) {
536                                         int len = dn_expand(buf, end, buf, (char*)entry->auxtarget, 256);
537                                         if (len < 1)
538                                                 break;
539 
540                                         buf = &buf[len];
541                                         entry->auxlen = strlen((char*)entry->auxtarget);
542 
543                                         if (entry->auxlen == 0)
544                                                 continue;
545 
546                                         changed |= odhcp6c_update_entry(STATE_RA_SEARCH, entry,
547                                                                         0, ra_holdoff_interval);
548                                         entry->auxlen = 0;
549                                 }
550                         }
551                 }
552 
553                 if (ra_options & RA_RDNSS_DEFAULT_LIFETIME) {
554                         int states[2] = {STATE_RA_DNS, STATE_RA_SEARCH};
555 
556                         for (size_t i = 0; i < 2; ++i) {
557                                 size_t ra_dns_len;
558                                 uint8_t *start = odhcp6c_get_state(states[i], &ra_dns_len);
559 
560                                 for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start;
561                                                         (uint8_t*)c < &start[ra_dns_len] &&
562                                                         (uint8_t*)odhcp6c_next_entry(c) <= &start[ra_dns_len];
563                                                         c = odhcp6c_next_entry(c)) {
564                                         if (IN6_ARE_ADDR_EQUAL(&c->router, &from.sin6_addr) &&
565                                                         c->valid > router_valid)
566                                                 c->valid = router_valid;
567                                 }
568                         }
569                 }
570         }
571 
572         if (found)
573                 odhcp6c_expire(false);
574 
575         return found && changed;
576 }
577 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt