xref: /f-stack/lib/ff_route.c (revision a02c88d6)
1 /*
2  * Copyright (c) 1988, 1991, 1993
3  *  The Regents of the University of California.  All rights reserved.
4  * Copyright (C) 2017 THL A29 Limited, a Tencent company.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 4. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * Copied part from FreeBSD rtsock.c.
32  *
33  */
34 
35 #include <sys/param.h>
36 #include <sys/proc.h>
37 #include <sys/jail.h>
38 #include <sys/kernel.h>
39 #include <sys/domain.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 
45 #include <net/if.h>
46 #include <net/if_var.h>
47 #include <net/if_dl.h>
48 #include <net/if_llatbl.h>
49 #include <net/if_types.h>
50 #include <net/route.h>
51 #include <net/route_var.h>
52 #include <netinet/if_ether.h>
53 
54 #include "ff_api.h"
55 #include "ff_host_interface.h"
56 
57 #ifndef _SOCKADDR_UNION_DEFINED
58 #define _SOCKADDR_UNION_DEFINED
59 /*
60  * The union of all possible address formats we handle.
61  */
62 union sockaddr_union {
63     struct sockaddr     sa;
64     struct sockaddr_in  sin;
65     struct sockaddr_in6 sin6;
66 };
67 #endif /* _SOCKADDR_UNION_DEFINED */
68 
69 static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, };
70 
71 struct walkarg {
72     int w_tmemsize;
73     int w_op, w_arg;
74     caddr_t w_tmem;
75     struct sysctl_req *w_req;
76 };
77 
78 static int
79 rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp,
80     struct rtentry *rt, union sockaddr_union *saun, struct ucred *cred)
81 {
82 
83     /* First, see if the returned address is part of the jail. */
84     if (prison_if(cred, rt->rt_ifa->ifa_addr) == 0) {
85         info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
86         return (0);
87     }
88 
89     switch (info->rti_info[RTAX_DST]->sa_family) {
90 #ifdef INET
91     case AF_INET:
92     {
93         struct in_addr ia;
94         struct ifaddr *ifa;
95         int found;
96 
97         found = 0;
98         /*
99          * Try to find an address on the given outgoing interface
100          * that belongs to the jail.
101          */
102         IF_ADDR_RLOCK(ifp);
103         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
104             struct sockaddr *sa;
105             sa = ifa->ifa_addr;
106             if (sa->sa_family != AF_INET)
107                 continue;
108             ia = ((struct sockaddr_in *)sa)->sin_addr;
109             if (prison_check_ip4(cred, &ia) == 0) {
110                 found = 1;
111                 break;
112             }
113         }
114         IF_ADDR_RUNLOCK(ifp);
115         if (!found) {
116             /*
117              * As a last resort return the 'default' jail address.
118              */
119             ia = ((struct sockaddr_in *)rt->rt_ifa->ifa_addr)->
120                 sin_addr;
121             if (prison_get_ip4(cred, &ia) != 0)
122                 return (ESRCH);
123         }
124         bzero(&saun->sin, sizeof(struct sockaddr_in));
125         saun->sin.sin_len = sizeof(struct sockaddr_in);
126         saun->sin.sin_family = AF_INET;
127         saun->sin.sin_addr.s_addr = ia.s_addr;
128         info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin;
129         break;
130     }
131 #endif
132 #ifdef INET6
133     case AF_INET6:
134     {
135         struct in6_addr ia6;
136         struct ifaddr *ifa;
137         int found;
138 
139         found = 0;
140         /*
141          * Try to find an address on the given outgoing interface
142          * that belongs to the jail.
143          */
144         IF_ADDR_RLOCK(ifp);
145         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
146             struct sockaddr *sa;
147             sa = ifa->ifa_addr;
148             if (sa->sa_family != AF_INET6)
149                 continue;
150             bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
151                 &ia6, sizeof(struct in6_addr));
152             if (prison_check_ip6(cred, &ia6) == 0) {
153                 found = 1;
154                 break;
155             }
156         }
157         IF_ADDR_RUNLOCK(ifp);
158         if (!found) {
159             /*
160              * As a last resort return the 'default' jail address.
161              */
162             ia6 = ((struct sockaddr_in6 *)rt->rt_ifa->ifa_addr)->
163                 sin6_addr;
164             if (prison_get_ip6(cred, &ia6) != 0)
165                 return (ESRCH);
166         }
167         bzero(&saun->sin6, sizeof(struct sockaddr_in6));
168         saun->sin6.sin6_len = sizeof(struct sockaddr_in6);
169         saun->sin6.sin6_family = AF_INET6;
170         bcopy(&ia6, &saun->sin6.sin6_addr, sizeof(struct in6_addr));
171         if (sa6_recoverscope(&saun->sin6) != 0)
172             return (ESRCH);
173         info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin6;
174         break;
175     }
176 #endif
177     default:
178         return (ESRCH);
179     }
180     return (0);
181 }
182 
183 /*
184  * Extract the addresses of the passed sockaddrs.
185  * Do a little sanity checking so as to avoid bad memory references.
186  * This data is derived straight from userland.
187  */
188 static int
189 rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
190 {
191     struct sockaddr *sa;
192     int i;
193 
194     for (i = 0; i < RTAX_MAX && cp < cplim; i++) {
195         if ((rtinfo->rti_addrs & (1 << i)) == 0)
196             continue;
197         sa = (struct sockaddr *)cp;
198         /*
199          * It won't fit.
200          */
201         if (cp + sa->sa_len > cplim)
202             return (EINVAL);
203         /*
204          * there are no more.. quit now
205          * If there are more bits, they are in error.
206          * I've seen this. route(1) can evidently generate these.
207          * This causes kernel to core dump.
208          * for compatibility, If we see this, point to a safe address.
209          */
210         if (sa->sa_len == 0) {
211             rtinfo->rti_info[i] = &sa_zero;
212             return (0); /* should be EINVAL but for compat */
213         }
214         /* accept it */
215 #ifdef INET6
216         if (sa->sa_family == AF_INET6)
217             sa6_embedscope((struct sockaddr_in6 *)sa,
218                 V_ip6_use_defzone);
219 #endif
220         rtinfo->rti_info[i] = sa;
221         cp += SA_SIZE(sa);
222     }
223     return (0);
224 }
225 
226 /*
227  * Writes information related to @rtinfo object to preallocated buffer.
228  * Stores needed size in @plen. If @w is NULL, calculates size without
229  * writing.
230  * Used for sysctl dumps and rtsock answers (RTM_DEL/RTM_GET) generation.
231  *
232  * Returns 0 on success.
233  *
234  */
235 static int
236 rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *plen)
237 {
238     int i;
239     int len, buflen = 0, dlen;
240     caddr_t cp = NULL;
241     struct rt_msghdr *rtm = NULL;
242 #ifdef INET6
243     struct sockaddr_storage ss;
244     struct sockaddr_in6 *sin6;
245 #endif
246 
247     switch (type) {
248 
249     case RTM_DELADDR:
250     case RTM_NEWADDR:
251         if (w != NULL && w->w_op == NET_RT_IFLISTL) {
252 #ifdef COMPAT_FREEBSD32
253             if (w->w_req->flags & SCTL_MASK32)
254                 len = sizeof(struct ifa_msghdrl32);
255             else
256 #endif
257                 len = sizeof(struct ifa_msghdrl);
258         } else
259             len = sizeof(struct ifa_msghdr);
260         break;
261 
262     case RTM_IFINFO:
263 #ifdef COMPAT_FREEBSD32
264         if (w != NULL && w->w_req->flags & SCTL_MASK32) {
265             if (w->w_op == NET_RT_IFLISTL)
266                 len = sizeof(struct if_msghdrl32);
267             else
268                 len = sizeof(struct if_msghdr32);
269             break;
270         }
271 #endif
272         if (w != NULL && w->w_op == NET_RT_IFLISTL)
273             len = sizeof(struct if_msghdrl);
274         else
275             len = sizeof(struct if_msghdr);
276         break;
277 
278     case RTM_NEWMADDR:
279         len = sizeof(struct ifma_msghdr);
280         break;
281 
282     default:
283         len = sizeof(struct rt_msghdr);
284     }
285 
286     if (w != NULL) {
287         rtm = (struct rt_msghdr *)w->w_tmem;
288         buflen = w->w_tmemsize - len;
289         cp = (caddr_t)w->w_tmem + len;
290     }
291 
292     rtinfo->rti_addrs = 0;
293     for (i = 0; i < RTAX_MAX; i++) {
294         struct sockaddr *sa;
295 
296         if ((sa = rtinfo->rti_info[i]) == NULL)
297             continue;
298         rtinfo->rti_addrs |= (1 << i);
299         dlen = SA_SIZE(sa);
300         if (cp != NULL && buflen >= dlen) {
301 #ifdef INET6
302             if (V_deembed_scopeid && sa->sa_family == AF_INET6) {
303                 sin6 = (struct sockaddr_in6 *)&ss;
304                 bcopy(sa, sin6, sizeof(*sin6));
305                 if (sa6_recoverscope(sin6) == 0)
306                     sa = (struct sockaddr *)sin6;
307             }
308 #endif
309             bcopy((caddr_t)sa, cp, (unsigned)dlen);
310             cp += dlen;
311             buflen -= dlen;
312         } else if (cp != NULL) {
313             /*
314              * Buffer too small. Count needed size
315              * and return with error.
316              */
317             cp = NULL;
318         }
319 
320         len += dlen;
321     }
322 
323     if (cp != NULL) {
324         dlen = ALIGN(len) - len;
325         if (buflen < dlen)
326             cp = NULL;
327         else
328             buflen -= dlen;
329     }
330     len = ALIGN(len);
331 
332     if (cp != NULL) {
333         /* fill header iff buffer is large enough */
334         rtm->rtm_version = RTM_VERSION;
335         rtm->rtm_type = type;
336         rtm->rtm_msglen = len;
337     }
338 
339     *plen = len;
340 
341     if (w != NULL && cp == NULL)
342         return (ENOBUFS);
343 
344     return (0);
345 }
346 
347 /*
348  * Fill in @dmask with valid netmask leaving original @smask
349  * intact. Mostly used with radix netmasks.
350  */
351 static struct sockaddr *
352 rtsock_fix_netmask(struct sockaddr *dst, struct sockaddr *smask,
353     struct sockaddr_storage *dmask)
354 {
355     if (dst == NULL || smask == NULL)
356         return (NULL);
357 
358     memset(dmask, 0, dst->sa_len);
359     memcpy(dmask, smask, smask->sa_len);
360     dmask->ss_len = dst->sa_len;
361     dmask->ss_family = dst->sa_family;
362 
363     return ((struct sockaddr *)dmask);
364 }
365 
366 static void
367 rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out)
368 {
369 
370     bzero(out, sizeof(*out));
371     out->rmx_mtu = rt->rt_mtu;
372     out->rmx_weight = rt->rt_weight;
373     out->rmx_pksent = counter_u64_fetch(rt->rt_pksent);
374     /* Kernel -> userland timebase conversion. */
375     out->rmx_expire = rt->rt_expire ?
376         rt->rt_expire - time_uptime + time_second : 0;
377 }
378 
379 int
380 ff_rtioctl(int fibnum, void *data, unsigned *plen, unsigned maxlen)
381 {
382     struct rt_msghdr *rtm = NULL;
383     struct rtentry *rt = NULL;
384     struct rib_head *rnh;
385     struct rt_addrinfo info;
386     union sockaddr_union saun;
387     sa_family_t saf = AF_UNSPEC;
388     struct sockaddr_storage ss;
389     struct walkarg w;
390     int error = 0, alloc_len = 0, len;
391     struct ifnet *ifp = NULL;
392 
393 #ifdef INET6
394     struct sockaddr_in6 *sin6;
395     int i, rti_need_deembed = 0;
396 #endif
397 
398 #define senderr(e) { error = e; goto flush;}
399 
400     len = *plen;
401     /*
402      * Most of current messages are in range 200-240 bytes,
403      * minimize possible re-allocation on reply using larger size
404      * buffer aligned on 1k boundaty.
405      */
406     alloc_len = roundup2(len, 1024);
407     if ((rtm = malloc(alloc_len, M_TEMP, M_NOWAIT)) == NULL)
408         senderr(ENOBUFS);
409     bcopy(data, (caddr_t)rtm, len);
410 
411     if (len < sizeof(*rtm) || len != rtm->rtm_msglen)
412         senderr(EINVAL);
413 
414     bzero(&info, sizeof(info));
415     bzero(&w, sizeof(w));
416 
417     if (rtm->rtm_version != RTM_VERSION)
418         senderr(EPROTONOSUPPORT);
419 
420     /*
421      * Starting from here, it is possible
422      * to alter original message and insert
423      * caller PID and error value.
424      */
425 
426     rtm->rtm_pid = curproc->p_pid;
427     info.rti_addrs = rtm->rtm_addrs;
428 
429     info.rti_mflags = rtm->rtm_inits;
430     info.rti_rmx = &rtm->rtm_rmx;
431 
432     /*
433      * rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6
434      * link-local address because rtrequest requires addresses with
435      * embedded scope id.
436      */
437     if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info))
438         senderr(EINVAL);
439 
440     info.rti_flags = rtm->rtm_flags;
441     if (info.rti_info[RTAX_DST] == NULL ||
442         info.rti_info[RTAX_DST]->sa_family >= AF_MAX ||
443         (info.rti_info[RTAX_GATEWAY] != NULL &&
444          info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX))
445         senderr(EINVAL);
446     saf = info.rti_info[RTAX_DST]->sa_family;
447 
448     /*
449      * The given gateway address may be an interface address.
450      * For example, issuing a "route change" command on a route
451      * entry that was created from a tunnel, and the gateway
452      * address given is the local end point. In this case the
453      * RTF_GATEWAY flag must be cleared or the destination will
454      * not be reachable even though there is no error message.
455      */
456     if (info.rti_info[RTAX_GATEWAY] != NULL &&
457         info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
458         struct rt_addrinfo ginfo;
459         struct sockaddr *gdst;
460 
461         bzero(&ginfo, sizeof(ginfo));
462         bzero(&ss, sizeof(ss));
463         ss.ss_len = sizeof(ss);
464 
465         ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
466         gdst = info.rti_info[RTAX_GATEWAY];
467 
468         /*
469          * A host route through the loopback interface is
470          * installed for each interface adddress. In pre 8.0
471          * releases the interface address of a PPP link type
472          * is not reachable locally. This behavior is fixed as
473          * part of the new L2/L3 redesign and rewrite work. The
474          * signature of this interface address route is the
475          * AF_LINK sa_family type of the rt_gateway, and the
476          * rt_ifp has the IFF_LOOPBACK flag set.
477          */
478         if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
479             if (ss.ss_family == AF_LINK &&
480                 ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
481                 info.rti_flags &= ~RTF_GATEWAY;
482                 info.rti_flags |= RTF_GWFLAG_COMPAT;
483             }
484             rib_free_info(&ginfo);
485         }
486     }
487 
488     switch (rtm->rtm_type) {
489         struct rtentry *saved_nrt;
490 
491     case RTM_ADD:
492     case RTM_CHANGE:
493         if (info.rti_info[RTAX_GATEWAY] == NULL)
494             senderr(EINVAL);
495         saved_nrt = NULL;
496 
497         /* support for new ARP code */
498         if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK &&
499             (rtm->rtm_flags & RTF_LLDATA) != 0) {
500             error = lla_rt_output(rtm, &info);
501 #ifdef INET6
502             if (error == 0)
503                 rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
504 #endif
505             break;
506         }
507         error = rtrequest1_fib(rtm->rtm_type, &info, &saved_nrt,
508             fibnum);
509         if (error == 0 && saved_nrt != NULL) {
510 #ifdef INET6
511             rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
512 #endif
513             RT_LOCK(saved_nrt);
514             rtm->rtm_index = saved_nrt->rt_ifp->if_index;
515             RT_REMREF(saved_nrt);
516             RT_UNLOCK(saved_nrt);
517         }
518         break;
519 
520     case RTM_DELETE:
521         saved_nrt = NULL;
522         /* support for new ARP code */
523         if (info.rti_info[RTAX_GATEWAY] &&
524             (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
525             (rtm->rtm_flags & RTF_LLDATA) != 0) {
526             error = lla_rt_output(rtm, &info);
527 #ifdef INET6
528             if (error == 0)
529                 rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
530 #endif
531             break;
532         }
533         error = rtrequest1_fib(RTM_DELETE, &info, &saved_nrt, fibnum);
534         if (error == 0) {
535             RT_LOCK(saved_nrt);
536             rt = saved_nrt;
537             goto report;
538         }
539 #ifdef INET6
540         /* rt_msg2() will not be used when RTM_DELETE fails. */
541         rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
542 #endif
543         break;
544 
545     case RTM_GET:
546         rnh = rt_tables_get_rnh(fibnum, saf);
547         if (rnh == NULL)
548             senderr(EAFNOSUPPORT);
549 
550         RIB_RLOCK(rnh);
551 
552         if (info.rti_info[RTAX_NETMASK] == NULL &&
553             rtm->rtm_type == RTM_GET) {
554             /*
555              * Provide logest prefix match for
556              * address lookup (no mask).
557              * 'route -n get addr'
558              */
559             rt = (struct rtentry *) rnh->rnh_matchaddr(
560                 info.rti_info[RTAX_DST], &rnh->head);
561         } else
562             rt = (struct rtentry *) rnh->rnh_lookup(
563                 info.rti_info[RTAX_DST],
564                 info.rti_info[RTAX_NETMASK], &rnh->head);
565 
566         if (rt == NULL) {
567             RIB_RUNLOCK(rnh);
568             senderr(ESRCH);
569         }
570 #ifdef RADIX_MPATH
571         /*
572          * for RTM_CHANGE/LOCK, if we got multipath routes,
573          * we require users to specify a matching RTAX_GATEWAY.
574          *
575          * for RTM_GET, gate is optional even with multipath.
576          * if gate == NULL the first match is returned.
577          * (no need to call rt_mpath_matchgate if gate == NULL)
578          */
579         if (rt_mpath_capable(rnh) &&
580             (rtm->rtm_type != RTM_GET || info.rti_info[RTAX_GATEWAY])) {
581             rt = rt_mpath_matchgate(rt, info.rti_info[RTAX_GATEWAY]);
582             if (!rt) {
583                 RIB_RUNLOCK(rnh);
584                 senderr(ESRCH);
585             }
586         }
587 #endif
588         /*
589          * If performing proxied L2 entry insertion, and
590          * the actual PPP host entry is found, perform
591          * another search to retrieve the prefix route of
592          * the local end point of the PPP link.
593          */
594         if (rtm->rtm_flags & RTF_ANNOUNCE) {
595             struct sockaddr laddr;
596 
597             if (rt->rt_ifp != NULL &&
598                 rt->rt_ifp->if_type == IFT_PROPVIRTUAL) {
599                 struct ifaddr *ifa;
600 
601                 ifa = ifa_ifwithnet(info.rti_info[RTAX_DST], 1,
602                         RT_ALL_FIBS);
603                 if (ifa != NULL)
604                     rt_maskedcopy(ifa->ifa_addr,
605                               &laddr,
606                               ifa->ifa_netmask);
607             } else
608                 rt_maskedcopy(rt->rt_ifa->ifa_addr,
609                           &laddr,
610                           rt->rt_ifa->ifa_netmask);
611             /*
612              * refactor rt and no lock operation necessary
613              */
614             rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr,
615                 &rnh->head);
616             if (rt == NULL) {
617                 RIB_RUNLOCK(rnh);
618                 senderr(ESRCH);
619             }
620         }
621         RT_LOCK(rt);
622         RT_ADDREF(rt);
623         RIB_RUNLOCK(rnh);
624 
625 report:
626         RT_LOCK_ASSERT(rt);
627         if ((rt->rt_flags & RTF_HOST) == 0
628             ? jailed_without_vnet(curthread->td_ucred)
629             : prison_if(curthread->td_ucred,
630             rt_key(rt)) != 0) {
631             RT_UNLOCK(rt);
632             senderr(ESRCH);
633         }
634         info.rti_info[RTAX_DST] = rt_key(rt);
635         info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
636         info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt),
637             rt_mask(rt), &ss);
638         info.rti_info[RTAX_GENMASK] = 0;
639         if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
640             ifp = rt->rt_ifp;
641             if (ifp) {
642                 info.rti_info[RTAX_IFP] =
643                     ifp->if_addr->ifa_addr;
644                 error = rtm_get_jailed(&info, ifp, rt,
645                     &saun, curthread->td_ucred);
646                 if (error != 0) {
647                     RT_UNLOCK(rt);
648                     senderr(error);
649                 }
650                 if (ifp->if_flags & IFF_POINTOPOINT)
651                     info.rti_info[RTAX_BRD] =
652                         rt->rt_ifa->ifa_dstaddr;
653                 rtm->rtm_index = ifp->if_index;
654             } else {
655                 info.rti_info[RTAX_IFP] = NULL;
656                 info.rti_info[RTAX_IFA] = NULL;
657             }
658         } else if ((ifp = rt->rt_ifp) != NULL) {
659             rtm->rtm_index = ifp->if_index;
660         }
661 
662         /* Check if we need to realloc storage */
663         rtsock_msg_buffer(rtm->rtm_type, &info, NULL, &len);
664         if (len > maxlen) {
665             RT_UNLOCK(rt);
666             senderr(ENOBUFS);
667         }
668 
669         if (len > alloc_len) {
670             struct rt_msghdr *new_rtm;
671             new_rtm = malloc(len, M_TEMP, M_NOWAIT);
672             if (new_rtm == NULL) {
673                 RT_UNLOCK(rt);
674                 senderr(ENOBUFS);
675             }
676             bcopy(rtm, new_rtm, rtm->rtm_msglen);
677             free(rtm, M_TEMP);
678             rtm = new_rtm;
679             alloc_len = len;
680         }
681 
682         w.w_tmem = (caddr_t)rtm;
683         w.w_tmemsize = alloc_len;
684         rtsock_msg_buffer(rtm->rtm_type, &info, &w, &len);
685 
686         if (rt->rt_flags & RTF_GWFLAG_COMPAT)
687             rtm->rtm_flags = RTF_GATEWAY |
688                 (rt->rt_flags & ~RTF_GWFLAG_COMPAT);
689         else
690             rtm->rtm_flags = rt->rt_flags;
691         rt_getmetrics(rt, &rtm->rtm_rmx);
692         rtm->rtm_addrs = info.rti_addrs;
693 
694         RT_UNLOCK(rt);
695         break;
696 
697     default:
698         senderr(EOPNOTSUPP);
699     }
700 
701 flush:
702     if (rt != NULL)
703         RTFREE(rt);
704 
705     if (rtm != NULL) {
706 #ifdef INET6
707         if (rti_need_deembed) {
708             /* sin6_scope_id is recovered before sending rtm. */
709             sin6 = (struct sockaddr_in6 *)&ss;
710             for (i = 0; i < RTAX_MAX; i++) {
711                 if (info.rti_info[i] == NULL)
712                     continue;
713                 if (info.rti_info[i]->sa_family != AF_INET6)
714                     continue;
715                 bcopy(info.rti_info[i], sin6, sizeof(*sin6));
716                 if (sa6_recoverscope(sin6) == 0)
717                     bcopy(sin6, info.rti_info[i],
718                             sizeof(*sin6));
719             }
720         }
721 #endif
722         if (error != 0)
723             rtm->rtm_errno = error;
724         else
725             rtm->rtm_flags |= RTF_DONE;
726 
727         bcopy((caddr_t)rtm, data, rtm->rtm_msglen);
728         *plen = rtm->rtm_msglen;
729         free(rtm, M_TEMP);
730     }
731 
732     return (error);
733 }
734