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