Merge branch 'tomato-ND-USBmod' into tomato-RT
[tomato.git] / release / src / router / radvd / send.c
blob1c24833d6554ebe740ce9f9d4ef39170105eda6f
1 /*
2 * $Id: send.c,v 1.48 2011/05/06 07:51:54 reubenhwk Exp $
4 * Authors:
5 * Pedro Roque <roque@di.fc.ul.pt>
6 * Lars Fenneberg <lf@elemental.net>
8 * This software is Copyright 1996,1997 by the above mentioned author(s),
9 * All Rights Reserved.
11 * The license which is distributed with this software in the file COPYRIGHT
12 * applies to this software. If your distribution is missing this file, you
13 * may request it from <pekkas@netcore.fi>.
17 #include "config.h"
18 #include "includes.h"
19 #include "radvd.h"
22 * Sends an advertisement for all specified clients of this interface
23 * (or via broadcast, if there are no restrictions configured).
25 * If a destination address is given, the RA will be sent to the destination
26 * address only, but only if it was configured.
29 int
30 send_ra_forall(struct Interface *iface, struct in6_addr *dest)
32 struct Clients *current;
34 /* If no list of clients was specified for this interface, we broadcast */
35 if (iface->ClientList == NULL)
36 return send_ra(iface, dest);
38 /* If clients are configured, send the advertisement to all of them via unicast */
39 for (current = iface->ClientList; current; current = current->next)
41 char address_text[INET6_ADDRSTRLEN];
42 memset(address_text, 0, sizeof(address_text));
43 if (get_debuglevel() >= 5)
44 inet_ntop(AF_INET6, &current->Address, address_text, INET6_ADDRSTRLEN);
46 /* If a non-authorized client sent a solicitation, ignore it (logging later) */
47 if (dest != NULL && memcmp(dest, &current->Address, sizeof(struct in6_addr)) != 0)
48 continue;
49 dlog(LOG_DEBUG, 5, "Sending RA to %s", address_text);
50 send_ra(iface, &(current->Address));
52 /* If we should only send the RA to a specific address, we are done */
53 if (dest != NULL)
54 return 0;
56 if (dest == NULL)
57 return 0;
59 /* If we refused a client's solicitation, log it if debugging is high enough */
60 char address_text[INET6_ADDRSTRLEN];
61 memset(address_text, 0, sizeof(address_text));
62 if (get_debuglevel() >= 5)
63 inet_ntop(AF_INET6, dest, address_text, INET6_ADDRSTRLEN);
65 dlog(LOG_DEBUG, 5, "Not answering request from %s, not configured", address_text);
66 return 0;
69 static void
70 send_ra_inc_len(size_t *len, int add)
72 *len += add;
73 if(*len >= MSG_SIZE_SEND)
75 flog(LOG_ERR, "Too many prefixes or routes. Exiting.");
76 exit(1);
80 static time_t
81 time_diff_secs(const struct timeval *time_x, const struct timeval *time_y)
83 time_t secs_diff;
85 secs_diff = time_x->tv_sec - time_y->tv_sec;
86 if ((time_x->tv_usec - time_y->tv_usec) >= 500000)
87 secs_diff++;
89 return secs_diff;
93 static void
94 decrement_lifetime(const time_t secs, uint32_t *lifetime)
97 if (*lifetime > secs) {
98 *lifetime -= secs;
99 } else {
100 *lifetime = 0;
104 static void cease_adv_pfx_msg(const char *if_name, struct in6_addr *pfx, const int pfx_len)
106 char pfx_str[INET6_ADDRSTRLEN];
108 print_addr(pfx, pfx_str);
110 dlog(LOG_DEBUG, 3, "Will cease advertising %s/%u%%%s, preferred lifetime is 0", pfx_str, pfx_len, if_name);
115 send_ra(struct Interface *iface, struct in6_addr *dest)
117 uint8_t all_hosts_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
118 struct sockaddr_in6 addr;
119 struct in6_pktinfo *pkt_info;
120 struct msghdr mhdr;
121 struct cmsghdr *cmsg;
122 struct iovec iov;
123 char __attribute__((aligned(8))) chdr[CMSG_SPACE(sizeof(struct in6_pktinfo))];
124 struct nd_router_advert *radvert;
125 struct AdvPrefix *prefix;
126 struct AdvRoute *route;
127 struct AdvRDNSS *rdnss;
128 struct AdvDNSSL *dnssl;
129 struct timeval time_now;
130 time_t secs_since_last_ra;
132 unsigned char buff[MSG_SIZE_SEND];
133 size_t len = 0;
134 ssize_t err;
136 /* First we need to check that the interface hasn't been removed or deactivated */
137 if(check_device(iface) < 0) {
138 if (iface->IgnoreIfMissing) /* a bit more quiet warning message.. */
139 dlog(LOG_DEBUG, 4, "interface %s does not exist, ignoring the interface", iface->Name);
140 else {
141 flog(LOG_WARNING, "interface %s does not exist, ignoring the interface", iface->Name);
143 iface->HasFailed = 1;
144 /* not really a 'success', but we need to schedule new timers.. */
145 return 0;
146 } else {
147 /* check_device was successful, act if it has failed previously */
148 if (iface->HasFailed == 1) {
149 flog(LOG_WARNING, "interface %s seems to have come back up, trying to reinitialize", iface->Name);
150 iface->HasFailed = 0;
152 * return -1 so timer_handler() doesn't schedule new timers,
153 * reload_config() will kick off new timers anyway. This avoids
154 * timer list corruption.
156 reload_config();
157 return -1;
161 /* Make sure that we've joined the all-routers multicast group */
162 if (check_allrouters_membership(iface) < 0)
163 flog(LOG_WARNING, "problem checking all-routers membership on %s", iface->Name);
165 dlog(LOG_DEBUG, 3, "sending RA on %s", iface->Name);
167 if (dest == NULL)
169 dest = (struct in6_addr *)all_hosts_addr;
170 gettimeofday(&iface->last_multicast, NULL);
173 gettimeofday(&time_now, NULL);
174 secs_since_last_ra = time_diff_secs(&time_now, &iface->last_ra_time);
175 if (secs_since_last_ra < 0) {
176 secs_since_last_ra = 0;
177 flog(LOG_WARNING, "gettimeofday() went backwards!");
179 iface->last_ra_time = time_now;
181 memset((void *)&addr, 0, sizeof(addr));
182 addr.sin6_family = AF_INET6;
183 addr.sin6_port = htons(IPPROTO_ICMPV6);
184 memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr));
186 memset(buff, 0, sizeof(buff));
187 radvert = (struct nd_router_advert *) buff;
189 radvert->nd_ra_type = ND_ROUTER_ADVERT;
190 radvert->nd_ra_code = 0;
191 radvert->nd_ra_cksum = 0;
193 radvert->nd_ra_curhoplimit = iface->AdvCurHopLimit;
194 radvert->nd_ra_flags_reserved =
195 (iface->AdvManagedFlag)?ND_RA_FLAG_MANAGED:0;
196 radvert->nd_ra_flags_reserved |=
197 (iface->AdvOtherConfigFlag)?ND_RA_FLAG_OTHER:0;
198 /* Mobile IPv6 ext */
199 radvert->nd_ra_flags_reserved |=
200 (iface->AdvHomeAgentFlag)?ND_RA_FLAG_HOME_AGENT:0;
202 if (iface->cease_adv) {
203 radvert->nd_ra_router_lifetime = 0;
204 } else {
205 /* if forwarding is disabled, send zero router lifetime */
206 radvert->nd_ra_router_lifetime = !check_ip6_forwarding() ? htons(iface->AdvDefaultLifetime) : 0;
208 radvert->nd_ra_flags_reserved |=
209 (iface->AdvDefaultPreference << ND_OPT_RI_PRF_SHIFT) & ND_OPT_RI_PRF_MASK;
211 radvert->nd_ra_reachable = htonl(iface->AdvReachableTime);
212 radvert->nd_ra_retransmit = htonl(iface->AdvRetransTimer);
214 len = sizeof(struct nd_router_advert);
216 prefix = iface->AdvPrefixList;
219 * add prefix options
222 while(prefix)
224 if( prefix->enabled && prefix->curr_preferredlft > 0 )
226 struct nd_opt_prefix_info *pinfo;
228 pinfo = (struct nd_opt_prefix_info *) (buff + len);
230 pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
231 pinfo->nd_opt_pi_len = 4;
232 pinfo->nd_opt_pi_prefix_len = prefix->PrefixLen;
234 pinfo->nd_opt_pi_flags_reserved =
235 (prefix->AdvOnLinkFlag)?ND_OPT_PI_FLAG_ONLINK:0;
236 pinfo->nd_opt_pi_flags_reserved |=
237 (prefix->AdvAutonomousFlag)?ND_OPT_PI_FLAG_AUTO:0;
238 /* Mobile IPv6 ext */
239 pinfo->nd_opt_pi_flags_reserved |=
240 (prefix->AdvRouterAddr)?ND_OPT_PI_FLAG_RADDR:0;
242 if (iface->cease_adv && prefix->DeprecatePrefixFlag) {
243 /* RFC4862, 5.5.3, step e) */
244 pinfo->nd_opt_pi_valid_time = htonl(MIN_AdvValidLifetime);
245 pinfo->nd_opt_pi_preferred_time = 0;
246 } else {
247 if (prefix->DecrementLifetimesFlag) {
248 decrement_lifetime(secs_since_last_ra,
249 &prefix->curr_validlft);
251 decrement_lifetime(secs_since_last_ra,
252 &prefix->curr_preferredlft);
253 if (prefix->curr_preferredlft == 0)
254 cease_adv_pfx_msg(iface->Name, &prefix->Prefix, prefix->PrefixLen);
256 pinfo->nd_opt_pi_valid_time = htonl(prefix->curr_validlft);
257 pinfo->nd_opt_pi_preferred_time = htonl(prefix->curr_preferredlft);
260 pinfo->nd_opt_pi_reserved2 = 0;
262 memcpy(&pinfo->nd_opt_pi_prefix, &prefix->Prefix,
263 sizeof(struct in6_addr));
265 send_ra_inc_len(&len, sizeof(*pinfo));
268 prefix = prefix->next;
271 route = iface->AdvRouteList;
274 * add route options
277 while(route)
279 struct nd_opt_route_info_local *rinfo;
281 rinfo = (struct nd_opt_route_info_local *) (buff + len);
283 rinfo->nd_opt_ri_type = ND_OPT_ROUTE_INFORMATION;
284 /* XXX: the prefixes are allowed to be sent in smaller chunks as well */
285 rinfo->nd_opt_ri_len = 3;
286 rinfo->nd_opt_ri_prefix_len = route->PrefixLen;
288 rinfo->nd_opt_ri_flags_reserved =
289 (route->AdvRoutePreference << ND_OPT_RI_PRF_SHIFT) & ND_OPT_RI_PRF_MASK;
290 if (iface->cease_adv && route->RemoveRouteFlag) {
291 rinfo->nd_opt_ri_lifetime = 0;
292 } else {
293 rinfo->nd_opt_ri_lifetime = htonl(route->AdvRouteLifetime);
296 memcpy(&rinfo->nd_opt_ri_prefix, &route->Prefix,
297 sizeof(struct in6_addr));
298 send_ra_inc_len(&len, sizeof(*rinfo));
300 route = route->next;
303 rdnss = iface->AdvRDNSSList;
306 * add rdnss options
309 while(rdnss)
311 struct nd_opt_rdnss_info_local *rdnssinfo;
313 rdnssinfo = (struct nd_opt_rdnss_info_local *) (buff + len);
315 rdnssinfo->nd_opt_rdnssi_type = ND_OPT_RDNSS_INFORMATION;
316 rdnssinfo->nd_opt_rdnssi_len = 1 + 2*rdnss->AdvRDNSSNumber;
317 rdnssinfo->nd_opt_rdnssi_pref_flag_reserved = 0;
319 if (iface->cease_adv && rdnss->FlushRDNSSFlag) {
320 rdnssinfo->nd_opt_rdnssi_lifetime = 0;
321 } else {
322 rdnssinfo->nd_opt_rdnssi_lifetime = htonl(rdnss->AdvRDNSSLifetime);
325 memcpy(&rdnssinfo->nd_opt_rdnssi_addr1, &rdnss->AdvRDNSSAddr1,
326 sizeof(struct in6_addr));
327 memcpy(&rdnssinfo->nd_opt_rdnssi_addr2, &rdnss->AdvRDNSSAddr2,
328 sizeof(struct in6_addr));
329 memcpy(&rdnssinfo->nd_opt_rdnssi_addr3, &rdnss->AdvRDNSSAddr3,
330 sizeof(struct in6_addr));
331 send_ra_inc_len(&len, sizeof(*rdnssinfo) - (3-rdnss->AdvRDNSSNumber)*sizeof(struct in6_addr));
333 rdnss = rdnss->next;
336 dnssl = iface->AdvDNSSLList;
339 * add dnssl options
342 while(dnssl)
344 struct nd_opt_dnssl_info_local *dnsslinfo;
345 int i;
346 char *buff_ptr;
348 dnsslinfo = (struct nd_opt_dnssl_info_local *) (buff + len);
350 dnsslinfo->nd_opt_dnssli_type = ND_OPT_DNSSL_INFORMATION;
351 dnsslinfo->nd_opt_dnssli_len = 1; /* more further down */
352 dnsslinfo->nd_opt_dnssli_reserved = 0;
354 if (iface->cease_adv && dnssl->FlushDNSSLFlag) {
355 dnsslinfo->nd_opt_dnssli_lifetime = 0;
356 } else {
357 dnsslinfo->nd_opt_dnssli_lifetime = htonl(dnssl->AdvDNSSLLifetime);
360 buff_ptr = dnsslinfo->nd_opt_dnssli_suffixes;
361 for (i = 0; i < dnssl->AdvDNSSLNumber; i++) {
362 char *label;
363 int label_len;
365 label = dnssl->AdvDNSSLSuffixes[i];
367 while (label[0] != '\0') {
368 if (strchr(label, '.') == NULL)
369 label_len = strlen(label);
370 else
371 label_len = strchr(label, '.') - label;
373 *buff_ptr++ = label_len;
375 memcpy(buff_ptr, label, label_len);
376 buff_ptr += label_len;
378 label += label_len;
380 if (label[0] == '.')
381 label++;
382 else
383 *buff_ptr++ = 0;
387 dnsslinfo->nd_opt_dnssli_len += ((buff_ptr-dnsslinfo->nd_opt_dnssli_suffixes)+7)/8;
389 send_ra_inc_len(&len, dnsslinfo->nd_opt_dnssli_len * 8);
391 dnssl = dnssl->next;
395 * add MTU option
398 if (iface->AdvLinkMTU != 0) {
399 struct nd_opt_mtu *mtu;
401 mtu = (struct nd_opt_mtu *) (buff + len);
403 mtu->nd_opt_mtu_type = ND_OPT_MTU;
404 mtu->nd_opt_mtu_len = 1;
405 mtu->nd_opt_mtu_reserved = 0;
406 mtu->nd_opt_mtu_mtu = htonl(iface->AdvLinkMTU);
408 send_ra_inc_len(&len, sizeof(*mtu));
412 * add Source Link-layer Address option
415 if (iface->AdvSourceLLAddress && iface->if_hwaddr_len > 0)
417 uint8_t *ucp;
418 unsigned int i;
420 ucp = (uint8_t *) (buff + len);
422 *ucp++ = ND_OPT_SOURCE_LINKADDR;
423 *ucp++ = (uint8_t) ((iface->if_hwaddr_len + 16 + 63) >> 6);
425 send_ra_inc_len(&len, 2 * sizeof(uint8_t));
427 i = (iface->if_hwaddr_len + 7) >> 3;
428 memcpy(buff + len, iface->if_hwaddr, i);
429 send_ra_inc_len(&len, i);
433 * Mobile IPv6 ext: Advertisement Interval Option to support
434 * movement detection of mobile nodes
437 if(iface->AdvIntervalOpt)
439 struct AdvInterval a_ival;
440 uint32_t ival;
441 if(iface->MaxRtrAdvInterval < Cautious_MaxRtrAdvInterval){
442 ival = ((iface->MaxRtrAdvInterval +
443 Cautious_MaxRtrAdvInterval_Leeway ) * 1000);
446 else {
447 ival = (iface->MaxRtrAdvInterval * 1000);
449 a_ival.type = ND_OPT_RTR_ADV_INTERVAL;
450 a_ival.length = 1;
451 a_ival.reserved = 0;
452 a_ival.adv_ival = htonl(ival);
454 memcpy(buff + len, &a_ival, sizeof(a_ival));
455 send_ra_inc_len(&len, sizeof(a_ival));
459 * Mobile IPv6 ext: Home Agent Information Option to support
460 * Dynamic Home Agent Address Discovery
463 if(iface->AdvHomeAgentInfo &&
464 (iface->AdvMobRtrSupportFlag || iface->HomeAgentPreference != 0 ||
465 iface->HomeAgentLifetime != iface->AdvDefaultLifetime))
468 struct HomeAgentInfo ha_info;
469 ha_info.type = ND_OPT_HOME_AGENT_INFO;
470 ha_info.length = 1;
471 ha_info.flags_reserved =
472 (iface->AdvMobRtrSupportFlag)?ND_OPT_HAI_FLAG_SUPPORT_MR:0;
473 ha_info.preference = htons(iface->HomeAgentPreference);
474 ha_info.lifetime = htons(iface->HomeAgentLifetime);
476 memcpy(buff + len, &ha_info, sizeof(ha_info));
477 send_ra_inc_len(&len, sizeof(ha_info));
480 iov.iov_len = len;
481 iov.iov_base = (caddr_t) buff;
483 memset(chdr, 0, sizeof(chdr));
484 cmsg = (struct cmsghdr *) chdr;
486 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
487 cmsg->cmsg_level = IPPROTO_IPV6;
488 cmsg->cmsg_type = IPV6_PKTINFO;
490 pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
491 pkt_info->ipi6_ifindex = iface->if_index;
492 memcpy(&pkt_info->ipi6_addr, &iface->if_addr, sizeof(struct in6_addr));
494 #ifdef HAVE_SIN6_SCOPE_ID
495 if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr) ||
496 IN6_IS_ADDR_MC_LINKLOCAL(&addr.sin6_addr))
497 addr.sin6_scope_id = iface->if_index;
498 #endif
500 memset(&mhdr, 0, sizeof(mhdr));
501 mhdr.msg_name = (caddr_t)&addr;
502 mhdr.msg_namelen = sizeof(struct sockaddr_in6);
503 mhdr.msg_iov = &iov;
504 mhdr.msg_iovlen = 1;
505 mhdr.msg_control = (void *) cmsg;
506 mhdr.msg_controllen = sizeof(chdr);
508 err = sendmsg(sock, &mhdr, 0);
510 if (err < 0) {
511 if (!iface->IgnoreIfMissing || !(errno == EINVAL || errno == ENODEV))
512 flog(LOG_WARNING, "sendmsg: %s", strerror(errno));
513 else
514 dlog(LOG_DEBUG, 3, "sendmsg: %s", strerror(errno));
517 return 0;