Commit | Line | Data |
---|---|---|
f3ed2586 JH |
1 | /* |
2 | * Copyright (c) 2004, 2005 The DragonFly Project. All rights reserved. | |
3 | * | |
4 | * This code is derived from software contributed to The DragonFly Project | |
5 | * by Jeffrey M. Hsu. | |
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 | * 3. Neither the name of The DragonFly Project nor the names of its | |
16 | * contributors may be used to endorse or promote products derived | |
17 | * from this software without specific, prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
24 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
25 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
27 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
29 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
30 | * SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Copyright (c) 2004, 2005 Jeffrey M. Hsu. All rights reserved. | |
35 | * | |
36 | * License terms: all terms for the DragonFly license above plus the following: | |
37 | * | |
38 | * 4. All advertising materials mentioning features or use of this software | |
39 | * must display the following acknowledgement: | |
40 | * | |
41 | * This product includes software developed by Jeffrey M. Hsu | |
42 | * for the DragonFly Project. | |
43 | * | |
44 | * This requirement may be waived with permission from Jeffrey Hsu. | |
45 | * Permission will be granted to any DragonFly user for free. | |
46 | * This requirement will sunset and may be removed on Jan 31, 2006, | |
47 | * after which the standard DragonFly license (as shown above) will | |
48 | * apply. | |
49 | */ | |
50 | ||
984263bc MD |
51 | /* |
52 | * Copyright (c) 1988, 1991, 1993 | |
53 | * The Regents of the University of California. All rights reserved. | |
54 | * | |
55 | * Redistribution and use in source and binary forms, with or without | |
56 | * modification, are permitted provided that the following conditions | |
57 | * are met: | |
58 | * 1. Redistributions of source code must retain the above copyright | |
59 | * notice, this list of conditions and the following disclaimer. | |
60 | * 2. Redistributions in binary form must reproduce the above copyright | |
61 | * notice, this list of conditions and the following disclaimer in the | |
62 | * documentation and/or other materials provided with the distribution. | |
63 | * 3. All advertising materials mentioning features or use of this software | |
64 | * must display the following acknowledgement: | |
65 | * This product includes software developed by the University of | |
66 | * California, Berkeley and its contributors. | |
67 | * 4. Neither the name of the University nor the names of its contributors | |
68 | * may be used to endorse or promote products derived from this software | |
69 | * without specific prior written permission. | |
70 | * | |
71 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
72 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
73 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
74 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
75 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
76 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
77 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
78 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
79 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
80 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
81 | * SUCH DAMAGE. | |
82 | * | |
83 | * @(#)rtsock.c 8.7 (Berkeley) 10/12/95 | |
84 | * $FreeBSD: src/sys/net/rtsock.c,v 1.44.2.11 2002/12/04 14:05:41 ru Exp $ | |
ecdefdda | 85 | * $DragonFly: src/sys/net/rtsock.c,v 1.30 2006/01/31 19:05:35 dillon Exp $ |
984263bc MD |
86 | */ |
87 | ||
78812139 EN |
88 | #include "opt_sctp.h" |
89 | ||
984263bc MD |
90 | #include <sys/param.h> |
91 | #include <sys/systm.h> | |
92 | #include <sys/kernel.h> | |
93 | #include <sys/sysctl.h> | |
94 | #include <sys/proc.h> | |
95 | #include <sys/malloc.h> | |
96 | #include <sys/mbuf.h> | |
47e78ae8 | 97 | #include <sys/protosw.h> |
984263bc MD |
98 | #include <sys/socket.h> |
99 | #include <sys/socketvar.h> | |
100 | #include <sys/domain.h> | |
a49aa710 | 101 | #include <sys/thread2.h> |
deffea2e | 102 | |
984263bc MD |
103 | #include <net/if.h> |
104 | #include <net/route.h> | |
105 | #include <net/raw_cb.h> | |
106 | ||
78812139 EN |
107 | #ifdef SCTP |
108 | extern void sctp_add_ip_address(struct ifaddr *ifa); | |
109 | extern void sctp_delete_ip_address(struct ifaddr *ifa); | |
110 | #endif /* SCTP */ | |
111 | ||
984263bc MD |
112 | MALLOC_DEFINE(M_RTABLE, "routetbl", "routing tables"); |
113 | ||
f23061d4 JH |
114 | static struct route_cb { |
115 | int ip_count; | |
116 | int ip6_count; | |
117 | int ipx_count; | |
118 | int ns_count; | |
119 | int any_count; | |
120 | } route_cb; | |
121 | ||
590b8cd4 | 122 | static const struct sockaddr route_src = { 2, PF_ROUTE, }; |
984263bc MD |
123 | |
124 | struct walkarg { | |
125 | int w_tmemsize; | |
126 | int w_op, w_arg; | |
8e63efec | 127 | void *w_tmem; |
984263bc MD |
128 | struct sysctl_req *w_req; |
129 | }; | |
130 | ||
131 | static struct mbuf * | |
590b8cd4 JH |
132 | rt_msg_mbuf (int, struct rt_addrinfo *); |
133 | static void rt_msg_buffer (int, struct rt_addrinfo *, void *buf, int len); | |
134 | static int rt_msgsize (int type, struct rt_addrinfo *rtinfo); | |
ef87f48d | 135 | static int rt_xaddrs (char *, char *, struct rt_addrinfo *); |
158abb01 RG |
136 | static int sysctl_dumpentry (struct radix_node *rn, void *vw); |
137 | static int sysctl_iflist (int af, struct walkarg *w); | |
deffea2e | 138 | static int route_output(struct mbuf *, struct socket *, ...); |
47e78ae8 | 139 | static void rt_setmetrics (u_long, struct rt_metrics *, |
f23061d4 | 140 | struct rt_metrics *); |
984263bc | 141 | |
984263bc MD |
142 | /* |
143 | * It really doesn't make any sense at all for this code to share much | |
144 | * with raw_usrreq.c, since its functionality is so restricted. XXX | |
145 | */ | |
146 | static int | |
147 | rts_abort(struct socket *so) | |
148 | { | |
a49aa710 | 149 | int error; |
f23061d4 | 150 | |
a49aa710 | 151 | crit_enter(); |
984263bc | 152 | error = raw_usrreqs.pru_abort(so); |
a49aa710 | 153 | crit_exit(); |
984263bc MD |
154 | return error; |
155 | } | |
156 | ||
157 | /* pru_accept is EOPNOTSUPP */ | |
158 | ||
159 | static int | |
e4700d00 | 160 | rts_attach(struct socket *so, int proto, struct pru_attach_info *ai) |
984263bc MD |
161 | { |
162 | struct rawcb *rp; | |
a49aa710 | 163 | int error; |
984263bc | 164 | |
ef87f48d | 165 | if (sotorawcb(so) != NULL) |
984263bc | 166 | return EISCONN; /* XXX panic? */ |
f23061d4 | 167 | |
590b8cd4 | 168 | rp = malloc(sizeof *rp, M_PCB, M_WAITOK | M_ZERO); |
ef87f48d | 169 | if (rp == NULL) |
984263bc MD |
170 | return ENOBUFS; |
171 | ||
172 | /* | |
a49aa710 | 173 | * The critical section is necessary to block protocols from sending |
984263bc MD |
174 | * error notifications (like RTM_REDIRECT or RTM_LOSING) while |
175 | * this PCB is extant but incompletely initialized. | |
176 | * Probably we should try to do more of this work beforehand and | |
a49aa710 | 177 | * eliminate the critical section. |
984263bc | 178 | */ |
a49aa710 | 179 | crit_enter(); |
ef87f48d | 180 | so->so_pcb = rp; |
e4700d00 | 181 | error = raw_attach(so, proto, ai->sb_rlimit); |
984263bc MD |
182 | rp = sotorawcb(so); |
183 | if (error) { | |
a49aa710 | 184 | crit_exit(); |
984263bc MD |
185 | free(rp, M_PCB); |
186 | return error; | |
187 | } | |
188 | switch(rp->rcb_proto.sp_protocol) { | |
189 | case AF_INET: | |
190 | route_cb.ip_count++; | |
191 | break; | |
192 | case AF_INET6: | |
193 | route_cb.ip6_count++; | |
194 | break; | |
195 | case AF_IPX: | |
196 | route_cb.ipx_count++; | |
197 | break; | |
198 | case AF_NS: | |
199 | route_cb.ns_count++; | |
200 | break; | |
201 | } | |
202 | rp->rcb_faddr = &route_src; | |
203 | route_cb.any_count++; | |
204 | soisconnected(so); | |
205 | so->so_options |= SO_USELOOPBACK; | |
a49aa710 | 206 | crit_exit(); |
984263bc MD |
207 | return 0; |
208 | } | |
209 | ||
210 | static int | |
dadab5e9 | 211 | rts_bind(struct socket *so, struct sockaddr *nam, struct thread *td) |
984263bc | 212 | { |
a49aa710 | 213 | int error; |
f23061d4 | 214 | |
a49aa710 | 215 | crit_enter(); |
dadab5e9 | 216 | error = raw_usrreqs.pru_bind(so, nam, td); /* xxx just EINVAL */ |
a49aa710 | 217 | crit_exit(); |
984263bc MD |
218 | return error; |
219 | } | |
220 | ||
221 | static int | |
dadab5e9 | 222 | rts_connect(struct socket *so, struct sockaddr *nam, struct thread *td) |
984263bc | 223 | { |
a49aa710 | 224 | int error; |
f23061d4 | 225 | |
a49aa710 | 226 | crit_enter(); |
dadab5e9 | 227 | error = raw_usrreqs.pru_connect(so, nam, td); /* XXX just EINVAL */ |
a49aa710 | 228 | crit_exit(); |
984263bc MD |
229 | return error; |
230 | } | |
231 | ||
232 | /* pru_connect2 is EOPNOTSUPP */ | |
233 | /* pru_control is EOPNOTSUPP */ | |
234 | ||
235 | static int | |
236 | rts_detach(struct socket *so) | |
237 | { | |
238 | struct rawcb *rp = sotorawcb(so); | |
a49aa710 | 239 | int error; |
984263bc | 240 | |
a49aa710 | 241 | crit_enter(); |
ef87f48d | 242 | if (rp != NULL) { |
984263bc MD |
243 | switch(rp->rcb_proto.sp_protocol) { |
244 | case AF_INET: | |
245 | route_cb.ip_count--; | |
246 | break; | |
247 | case AF_INET6: | |
248 | route_cb.ip6_count--; | |
249 | break; | |
250 | case AF_IPX: | |
251 | route_cb.ipx_count--; | |
252 | break; | |
253 | case AF_NS: | |
254 | route_cb.ns_count--; | |
255 | break; | |
256 | } | |
257 | route_cb.any_count--; | |
258 | } | |
259 | error = raw_usrreqs.pru_detach(so); | |
a49aa710 | 260 | crit_exit(); |
984263bc MD |
261 | return error; |
262 | } | |
263 | ||
264 | static int | |
265 | rts_disconnect(struct socket *so) | |
266 | { | |
a49aa710 | 267 | int error; |
f23061d4 | 268 | |
a49aa710 | 269 | crit_enter(); |
984263bc | 270 | error = raw_usrreqs.pru_disconnect(so); |
a49aa710 | 271 | crit_exit(); |
984263bc MD |
272 | return error; |
273 | } | |
274 | ||
275 | /* pru_listen is EOPNOTSUPP */ | |
276 | ||
277 | static int | |
278 | rts_peeraddr(struct socket *so, struct sockaddr **nam) | |
279 | { | |
a49aa710 | 280 | int error; |
f23061d4 | 281 | |
a49aa710 | 282 | crit_enter(); |
984263bc | 283 | error = raw_usrreqs.pru_peeraddr(so, nam); |
a49aa710 | 284 | crit_exit(); |
984263bc MD |
285 | return error; |
286 | } | |
287 | ||
288 | /* pru_rcvd is EOPNOTSUPP */ | |
289 | /* pru_rcvoob is EOPNOTSUPP */ | |
290 | ||
291 | static int | |
292 | rts_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, | |
dadab5e9 | 293 | struct mbuf *control, struct thread *td) |
984263bc | 294 | { |
a49aa710 | 295 | int error; |
f23061d4 | 296 | |
a49aa710 | 297 | crit_enter(); |
dadab5e9 | 298 | error = raw_usrreqs.pru_send(so, flags, m, nam, control, td); |
a49aa710 | 299 | crit_exit(); |
984263bc MD |
300 | return error; |
301 | } | |
302 | ||
303 | /* pru_sense is null */ | |
304 | ||
305 | static int | |
306 | rts_shutdown(struct socket *so) | |
307 | { | |
a49aa710 | 308 | int error; |
f23061d4 | 309 | |
a49aa710 | 310 | crit_enter(); |
984263bc | 311 | error = raw_usrreqs.pru_shutdown(so); |
a49aa710 | 312 | crit_exit(); |
984263bc MD |
313 | return error; |
314 | } | |
315 | ||
316 | static int | |
317 | rts_sockaddr(struct socket *so, struct sockaddr **nam) | |
318 | { | |
a49aa710 | 319 | int error; |
f23061d4 | 320 | |
a49aa710 | 321 | crit_enter(); |
984263bc | 322 | error = raw_usrreqs.pru_sockaddr(so, nam); |
a49aa710 | 323 | crit_exit(); |
984263bc MD |
324 | return error; |
325 | } | |
326 | ||
327 | static struct pr_usrreqs route_usrreqs = { | |
328 | rts_abort, pru_accept_notsupp, rts_attach, rts_bind, rts_connect, | |
329 | pru_connect2_notsupp, pru_control_notsupp, rts_detach, rts_disconnect, | |
330 | pru_listen_notsupp, rts_peeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, | |
331 | rts_send, pru_sense_null, rts_shutdown, rts_sockaddr, | |
332 | sosend, soreceive, sopoll | |
333 | }; | |
334 | ||
590b8cd4 JH |
335 | static __inline sa_family_t |
336 | familyof(struct sockaddr *sa) | |
337 | { | |
338 | return (sa != NULL ? sa->sa_family : 0); | |
339 | } | |
340 | ||
341 | static void | |
342 | rts_input(struct mbuf *m, sa_family_t family) | |
343 | { | |
344 | static const struct sockaddr route_dst = { 2, PF_ROUTE, }; | |
345 | struct sockproto route_proto = { PF_ROUTE, family }; | |
346 | ||
347 | raw_input(m, &route_proto, &route_src, &route_dst); | |
348 | } | |
349 | ||
350 | static void * | |
351 | reallocbuf(void *ptr, size_t len, size_t olen) | |
352 | { | |
353 | void *newptr; | |
354 | ||
355 | newptr = malloc(len, M_RTABLE, M_INTWAIT | M_NULLOK); | |
356 | if (newptr == NULL) | |
357 | return NULL; | |
358 | bcopy(ptr, newptr, olen); | |
359 | free(ptr, M_RTABLE); | |
360 | return (newptr); | |
361 | } | |
362 | ||
8e63efec JH |
363 | /* |
364 | * Internal helper routine for route_output(). | |
365 | */ | |
590b8cd4 JH |
366 | static int |
367 | fillrtmsg(struct rt_msghdr **prtm, struct rtentry *rt, | |
368 | struct rt_addrinfo *rtinfo) | |
369 | { | |
370 | int msglen; | |
371 | struct rt_msghdr *rtm = *prtm; | |
372 | ||
373 | /* Fill in rt_addrinfo for call to rt_msg_buffer(). */ | |
374 | rtinfo->rti_dst = rt_key(rt); | |
375 | rtinfo->rti_gateway = rt->rt_gateway; | |
376 | rtinfo->rti_netmask = rt_mask(rt); /* might be NULL */ | |
377 | rtinfo->rti_genmask = rt->rt_genmask; /* might be NULL */ | |
378 | if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { | |
379 | if (rt->rt_ifp != NULL) { | |
380 | rtinfo->rti_ifpaddr = | |
381 | TAILQ_FIRST(&rt->rt_ifp->if_addrhead)->ifa_addr; | |
382 | rtinfo->rti_ifaaddr = rt->rt_ifa->ifa_addr; | |
383 | if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) | |
384 | rtinfo->rti_bcastaddr = rt->rt_ifa->ifa_dstaddr; | |
385 | rtm->rtm_index = rt->rt_ifp->if_index; | |
386 | } else { | |
387 | rtinfo->rti_ifpaddr = NULL; | |
388 | rtinfo->rti_ifaaddr = NULL; | |
389 | } | |
390 | } | |
391 | ||
392 | msglen = rt_msgsize(rtm->rtm_type, rtinfo); | |
393 | if (rtm->rtm_msglen < msglen) { | |
394 | rtm = reallocbuf(rtm, msglen, rtm->rtm_msglen); | |
395 | if (rtm == NULL) | |
396 | return (ENOBUFS); | |
397 | *prtm = rtm; | |
398 | } | |
399 | rt_msg_buffer(rtm->rtm_type, rtinfo, rtm, msglen); | |
400 | ||
401 | rtm->rtm_flags = rt->rt_flags; | |
402 | rtm->rtm_rmx = rt->rt_rmx; | |
403 | rtm->rtm_addrs = rtinfo->rti_addrs; | |
404 | ||
405 | return (0); | |
406 | } | |
407 | ||
ecdefdda MD |
408 | static void route_output_add_callback(int, int, struct rt_addrinfo *, |
409 | struct rtentry *, void *); | |
410 | static void route_output_delete_callback(int, int, struct rt_addrinfo *, | |
411 | struct rtentry *, void *); | |
412 | static void route_output_change_callback(int, int, struct rt_addrinfo *, | |
413 | struct rtentry *, void *); | |
414 | static void route_output_lock_callback(int, int, struct rt_addrinfo *, | |
415 | struct rtentry *, void *); | |
416 | ||
984263bc MD |
417 | /*ARGSUSED*/ |
418 | static int | |
deffea2e | 419 | route_output(struct mbuf *m, struct socket *so, ...) |
984263bc | 420 | { |
2e9572df | 421 | struct rt_msghdr *rtm = NULL; |
ecdefdda | 422 | struct rtentry *rt; |
984263bc | 423 | struct radix_node_head *rnh; |
f23061d4 | 424 | struct rawcb *rp = NULL; |
deffea2e | 425 | struct pr_output_info *oi; |
590b8cd4 | 426 | struct rt_addrinfo rtinfo; |
ef87f48d | 427 | int len, error = 0; |
deffea2e JS |
428 | __va_list ap; |
429 | ||
430 | __va_start(ap, so); | |
431 | oi = __va_arg(ap, struct pr_output_info *); | |
432 | __va_end(ap); | |
984263bc | 433 | |
ef87f48d | 434 | #define gotoerr(e) { error = e; goto flush;} |
590b8cd4 JH |
435 | |
436 | if (m == NULL || | |
437 | (m->m_len < sizeof(long) && | |
438 | (m = m_pullup(m, sizeof(long))) == NULL)) | |
984263bc | 439 | return (ENOBUFS); |
ef87f48d | 440 | if (!(m->m_flags & M_PKTHDR)) |
984263bc MD |
441 | panic("route_output"); |
442 | len = m->m_pkthdr.len; | |
590b8cd4 | 443 | if (len < sizeof(struct rt_msghdr) || |
984263bc | 444 | len != mtod(m, struct rt_msghdr *)->rtm_msglen) { |
590b8cd4 | 445 | rtinfo.rti_dst = NULL; |
ef87f48d | 446 | gotoerr(EINVAL); |
984263bc | 447 | } |
590b8cd4 | 448 | rtm = malloc(len, M_RTABLE, M_INTWAIT | M_NULLOK); |
ef87f48d | 449 | if (rtm == NULL) { |
590b8cd4 | 450 | rtinfo.rti_dst = NULL; |
ef87f48d | 451 | gotoerr(ENOBUFS); |
984263bc MD |
452 | } |
453 | m_copydata(m, 0, len, (caddr_t)rtm); | |
454 | if (rtm->rtm_version != RTM_VERSION) { | |
590b8cd4 | 455 | rtinfo.rti_dst = NULL; |
ef87f48d | 456 | gotoerr(EPROTONOSUPPORT); |
984263bc | 457 | } |
47e78ae8 | 458 | rtm->rtm_pid = oi->p_pid; |
590b8cd4 JH |
459 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
460 | rtinfo.rti_addrs = rtm->rtm_addrs; | |
461 | if (rt_xaddrs((char *)(rtm + 1), (char *)rtm + len, &rtinfo) != 0) { | |
462 | rtinfo.rti_dst = NULL; | |
ef87f48d | 463 | gotoerr(EINVAL); |
984263bc | 464 | } |
590b8cd4 JH |
465 | rtinfo.rti_flags = rtm->rtm_flags; |
466 | if (rtinfo.rti_dst == NULL || rtinfo.rti_dst->sa_family >= AF_MAX || | |
467 | (rtinfo.rti_gateway && rtinfo.rti_gateway->sa_family >= AF_MAX)) | |
ef87f48d JH |
468 | gotoerr(EINVAL); |
469 | ||
590b8cd4 JH |
470 | if (rtinfo.rti_genmask != NULL) { |
471 | struct radix_node *n; | |
ef87f48d | 472 | |
590b8cd4 JH |
473 | #define clen(s) (*(u_char *)(s)) |
474 | n = rn_addmask((char *)rtinfo.rti_genmask, TRUE, 1); | |
475 | if (n != NULL && | |
476 | rtinfo.rti_genmask->sa_len >= clen(n->rn_key) && | |
477 | bcmp((char *)rtinfo.rti_genmask + 1, | |
478 | (char *)n->rn_key + 1, clen(n->rn_key) - 1) == 0) | |
479 | rtinfo.rti_genmask = (struct sockaddr *)n->rn_key; | |
984263bc | 480 | else |
ef87f48d | 481 | gotoerr(ENOBUFS); |
984263bc MD |
482 | } |
483 | ||
484 | /* | |
485 | * Verify that the caller has the appropriate privilege; RTM_GET | |
486 | * is the only operation the non-superuser is allowed. | |
487 | */ | |
dadab5e9 | 488 | if (rtm->rtm_type != RTM_GET && suser_cred(so->so_cred, 0) != 0) |
ef87f48d | 489 | gotoerr(EPERM); |
984263bc MD |
490 | |
491 | switch (rtm->rtm_type) { | |
984263bc | 492 | case RTM_ADD: |
ecdefdda MD |
493 | if (rtinfo.rti_gateway == NULL) { |
494 | error = EINVAL; | |
495 | } else { | |
496 | error = rtrequest1_global(RTM_ADD, &rtinfo, | |
497 | route_output_add_callback, rtm); | |
984263bc MD |
498 | } |
499 | break; | |
984263bc | 500 | case RTM_DELETE: |
ecdefdda MD |
501 | /* |
502 | * note: &rtm passed as argument so 'rtm' can be replaced. | |
503 | */ | |
504 | error = rtrequest1_global(RTM_DELETE, &rtinfo, | |
505 | route_output_delete_callback, &rtm); | |
984263bc | 506 | break; |
984263bc | 507 | case RTM_GET: |
ecdefdda MD |
508 | rnh = rt_tables[mycpuid][rtinfo.rti_dst->sa_family]; |
509 | if (rnh == NULL) { | |
510 | error = EAFNOSUPPORT; | |
511 | break; | |
512 | } | |
590b8cd4 JH |
513 | rt = (struct rtentry *) |
514 | rnh->rnh_lookup((char *)rtinfo.rti_dst, | |
515 | (char *)rtinfo.rti_netmask, rnh); | |
ecdefdda MD |
516 | if (rt == NULL) { |
517 | error = ESRCH; | |
984263bc MD |
518 | break; |
519 | } | |
ecdefdda MD |
520 | rt->rt_refcnt++; |
521 | if (fillrtmsg(&rtm, rt, &rtinfo) != 0) | |
522 | gotoerr(ENOBUFS); | |
523 | --rt->rt_refcnt; | |
524 | break; | |
525 | case RTM_CHANGE: | |
526 | error = rtrequest1_global(RTM_GET, &rtinfo, | |
527 | route_output_change_callback, rtm); | |
528 | break; | |
529 | case RTM_LOCK: | |
530 | error = rtrequest1_global(RTM_GET, &rtinfo, | |
531 | route_output_lock_callback, rtm); | |
590b8cd4 | 532 | break; |
984263bc | 533 | default: |
ecdefdda MD |
534 | error = EOPNOTSUPP; |
535 | break; | |
984263bc MD |
536 | } |
537 | ||
538 | flush: | |
590b8cd4 JH |
539 | if (rtm != NULL) { |
540 | if (error != 0) | |
984263bc MD |
541 | rtm->rtm_errno = error; |
542 | else | |
543 | rtm->rtm_flags |= RTF_DONE; | |
544 | } | |
ecdefdda | 545 | |
984263bc MD |
546 | /* |
547 | * Check to see if we don't want our own messages. | |
548 | */ | |
ef87f48d | 549 | if (!(so->so_options & SO_USELOOPBACK)) { |
984263bc | 550 | if (route_cb.any_count <= 1) { |
590b8cd4 JH |
551 | if (rtm != NULL) |
552 | free(rtm, M_RTABLE); | |
984263bc MD |
553 | m_freem(m); |
554 | return (error); | |
555 | } | |
556 | /* There is another listener, so construct message */ | |
557 | rp = sotorawcb(so); | |
558 | } | |
590b8cd4 | 559 | if (rtm != NULL) { |
984263bc MD |
560 | m_copyback(m, 0, rtm->rtm_msglen, (caddr_t)rtm); |
561 | if (m->m_pkthdr.len < rtm->rtm_msglen) { | |
562 | m_freem(m); | |
563 | m = NULL; | |
564 | } else if (m->m_pkthdr.len > rtm->rtm_msglen) | |
565 | m_adj(m, rtm->rtm_msglen - m->m_pkthdr.len); | |
590b8cd4 | 566 | free(rtm, M_RTABLE); |
984263bc | 567 | } |
ef87f48d | 568 | if (rp != NULL) |
984263bc | 569 | rp->rcb_proto.sp_family = 0; /* Avoid us */ |
ef87f48d | 570 | if (m != NULL) |
590b8cd4 | 571 | rts_input(m, familyof(rtinfo.rti_dst)); |
ef87f48d | 572 | if (rp != NULL) |
984263bc | 573 | rp->rcb_proto.sp_family = PF_ROUTE; |
984263bc MD |
574 | return (error); |
575 | } | |
576 | ||
ecdefdda MD |
577 | static void |
578 | route_output_add_callback(int cmd, int error, struct rt_addrinfo *rtinfo, | |
579 | struct rtentry *rt, void *arg) | |
580 | { | |
581 | struct rt_msghdr *rtm = arg; | |
582 | ||
583 | if (error == 0 && rt != NULL) { | |
584 | rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, | |
585 | &rt->rt_rmx); | |
586 | rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); | |
587 | rt->rt_rmx.rmx_locks |= | |
588 | (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks); | |
589 | rt->rt_genmask = rtinfo->rti_genmask; | |
590 | } | |
591 | } | |
592 | ||
593 | static void | |
594 | route_output_delete_callback(int cmd, int error, struct rt_addrinfo *rtinfo, | |
595 | struct rtentry *rt, void *arg) | |
596 | { | |
597 | struct rt_msghdr **rtm = arg; | |
598 | ||
599 | if (error == 0 && rt) { | |
600 | ++rt->rt_refcnt; | |
601 | if (fillrtmsg(rtm, rt, rtinfo) != 0) { | |
602 | error = ENOBUFS; | |
603 | /* XXX no way to return the error */ | |
604 | } | |
605 | --rt->rt_refcnt; | |
606 | } | |
607 | } | |
608 | ||
609 | static void | |
610 | route_output_change_callback(int cmd, int error, struct rt_addrinfo *rtinfo, | |
611 | struct rtentry *rt, void *arg) | |
612 | { | |
613 | struct rt_msghdr *rtm = arg; | |
614 | struct ifaddr *ifa; | |
615 | ||
616 | if (error) | |
617 | goto done; | |
618 | ||
619 | /* | |
620 | * new gateway could require new ifaddr, ifp; | |
621 | * flags may also be different; ifp may be specified | |
622 | * by ll sockaddr when protocol address is ambiguous | |
623 | */ | |
624 | if (((rt->rt_flags & RTF_GATEWAY) && rtinfo->rti_gateway != NULL) || | |
625 | rtinfo->rti_ifpaddr != NULL || (rtinfo->rti_ifaaddr != NULL && | |
626 | sa_equal(rtinfo->rti_ifaaddr, rt->rt_ifa->ifa_addr)) | |
627 | ) { | |
628 | error = rt_getifa(rtinfo); | |
629 | if (error != 0) | |
630 | goto done; | |
631 | } | |
632 | if (rtinfo->rti_gateway != NULL) { | |
633 | error = rt_setgate(rt, rt_key(rt), rtinfo->rti_gateway); | |
634 | if (error != 0) | |
635 | goto done; | |
636 | } | |
637 | if ((ifa = rtinfo->rti_ifa) != NULL) { | |
638 | struct ifaddr *oifa = rt->rt_ifa; | |
639 | ||
640 | if (oifa != ifa) { | |
641 | if (oifa && oifa->ifa_rtrequest) | |
642 | oifa->ifa_rtrequest(RTM_DELETE, rt, rtinfo); | |
643 | IFAFREE(rt->rt_ifa); | |
644 | IFAREF(ifa); | |
645 | rt->rt_ifa = ifa; | |
646 | rt->rt_ifp = rtinfo->rti_ifp; | |
647 | } | |
648 | } | |
649 | rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, &rt->rt_rmx); | |
650 | if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest) | |
651 | rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, rtinfo); | |
652 | if (rtinfo->rti_genmask != NULL) | |
653 | rt->rt_genmask = rtinfo->rti_genmask; | |
654 | done: | |
655 | /* XXX no way to return error */ | |
656 | ; | |
657 | } | |
658 | ||
659 | static void | |
660 | route_output_lock_callback(int cmd, int error, struct rt_addrinfo *rtinfo, | |
661 | struct rtentry *rt, void *arg) | |
662 | { | |
663 | struct rt_msghdr *rtm = arg; | |
664 | ||
665 | rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); | |
666 | rt->rt_rmx.rmx_locks |= | |
667 | (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks); | |
668 | } | |
669 | ||
984263bc | 670 | static void |
ef87f48d | 671 | rt_setmetrics(u_long which, struct rt_metrics *in, struct rt_metrics *out) |
984263bc | 672 | { |
2e9572df JH |
673 | #define setmetric(flag, elt) if (which & (flag)) out->elt = in->elt; |
674 | setmetric(RTV_RPIPE, rmx_recvpipe); | |
675 | setmetric(RTV_SPIPE, rmx_sendpipe); | |
676 | setmetric(RTV_SSTHRESH, rmx_ssthresh); | |
677 | setmetric(RTV_RTT, rmx_rtt); | |
678 | setmetric(RTV_RTTVAR, rmx_rttvar); | |
679 | setmetric(RTV_HOPCOUNT, rmx_hopcount); | |
680 | setmetric(RTV_MTU, rmx_mtu); | |
681 | setmetric(RTV_EXPIRE, rmx_expire); | |
682 | #undef setmetric | |
984263bc MD |
683 | } |
684 | ||
685 | #define ROUNDUP(a) \ | |
686 | ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) | |
984263bc | 687 | |
984263bc MD |
688 | /* |
689 | * Extract the addresses of the passed sockaddrs. | |
690 | * Do a little sanity checking so as to avoid bad memory references. | |
691 | * This data is derived straight from userland. | |
692 | */ | |
693 | static int | |
ef87f48d | 694 | rt_xaddrs(char *cp, char *cplim, struct rt_addrinfo *rtinfo) |
984263bc | 695 | { |
82ed7fc2 RG |
696 | struct sockaddr *sa; |
697 | int i; | |
984263bc MD |
698 | |
699 | for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { | |
700 | if ((rtinfo->rti_addrs & (1 << i)) == 0) | |
701 | continue; | |
702 | sa = (struct sockaddr *)cp; | |
703 | /* | |
704 | * It won't fit. | |
705 | */ | |
0c3c561c | 706 | if ((cp + sa->sa_len) > cplim) { |
984263bc MD |
707 | return (EINVAL); |
708 | } | |
709 | ||
710 | /* | |
ef87f48d | 711 | * There are no more... Quit now. |
984263bc | 712 | * If there are more bits, they are in error. |
ef87f48d | 713 | * I've seen this. route(1) can evidently generate these. |
984263bc | 714 | * This causes kernel to core dump. |
ef87f48d | 715 | * For compatibility, if we see this, point to a safe address. |
984263bc MD |
716 | */ |
717 | if (sa->sa_len == 0) { | |
590b8cd4 JH |
718 | static struct sockaddr sa_zero = { |
719 | sizeof sa_zero, AF_INET, | |
720 | }; | |
721 | ||
984263bc MD |
722 | rtinfo->rti_info[i] = &sa_zero; |
723 | return (0); /* should be EINVAL but for compat */ | |
724 | } | |
725 | ||
ef87f48d | 726 | /* Accept the sockaddr. */ |
984263bc | 727 | rtinfo->rti_info[i] = sa; |
590b8cd4 | 728 | cp += ROUNDUP(sa->sa_len); |
984263bc MD |
729 | } |
730 | return (0); | |
731 | } | |
732 | ||
590b8cd4 JH |
733 | static int |
734 | rt_msghdrsize(int type) | |
984263bc | 735 | { |
984263bc | 736 | switch (type) { |
984263bc MD |
737 | case RTM_DELADDR: |
738 | case RTM_NEWADDR: | |
590b8cd4 | 739 | return sizeof(struct ifa_msghdr); |
984263bc MD |
740 | case RTM_DELMADDR: |
741 | case RTM_NEWMADDR: | |
590b8cd4 | 742 | return sizeof(struct ifma_msghdr); |
984263bc | 743 | case RTM_IFINFO: |
590b8cd4 | 744 | return sizeof(struct if_msghdr); |
984263bc | 745 | case RTM_IFANNOUNCE: |
590b8cd4 | 746 | return sizeof(struct if_announcemsghdr); |
984263bc | 747 | default: |
590b8cd4 | 748 | return sizeof(struct rt_msghdr); |
984263bc | 749 | } |
590b8cd4 JH |
750 | } |
751 | ||
752 | static int | |
753 | rt_msgsize(int type, struct rt_addrinfo *rtinfo) | |
754 | { | |
755 | int len, i; | |
756 | ||
757 | len = rt_msghdrsize(type); | |
758 | for (i = 0; i < RTAX_MAX; i++) { | |
759 | if (rtinfo->rti_info[i] != NULL) | |
760 | len += ROUNDUP(rtinfo->rti_info[i]->sa_len); | |
984263bc | 761 | } |
590b8cd4 JH |
762 | len = ALIGN(len); |
763 | return len; | |
764 | } | |
765 | ||
766 | /* | |
767 | * Build a routing message in a buffer. | |
768 | * Copy the addresses in the rtinfo->rti_info[] sockaddr array | |
769 | * to the end of the buffer after the message header. | |
770 | * | |
771 | * Set the rtinfo->rti_addrs bitmask of addresses present in rtinfo->rti_info[]. | |
772 | * This side-effect can be avoided if we reorder the addrs bitmask field in all | |
773 | * the route messages to line up so we can set it here instead of back in the | |
774 | * calling routine. | |
775 | */ | |
776 | static void | |
777 | rt_msg_buffer(int type, struct rt_addrinfo *rtinfo, void *buf, int msglen) | |
778 | { | |
779 | struct rt_msghdr *rtm; | |
780 | char *cp; | |
781 | int dlen, i; | |
782 | ||
783 | rtm = (struct rt_msghdr *) buf; | |
784 | rtm->rtm_version = RTM_VERSION; | |
785 | rtm->rtm_type = type; | |
786 | rtm->rtm_msglen = msglen; | |
787 | ||
788 | cp = (char *)buf + rt_msghdrsize(type); | |
789 | rtinfo->rti_addrs = 0; | |
984263bc | 790 | for (i = 0; i < RTAX_MAX; i++) { |
590b8cd4 JH |
791 | struct sockaddr *sa; |
792 | ||
984263bc MD |
793 | if ((sa = rtinfo->rti_info[i]) == NULL) |
794 | continue; | |
795 | rtinfo->rti_addrs |= (1 << i); | |
796 | dlen = ROUNDUP(sa->sa_len); | |
590b8cd4 JH |
797 | bcopy(sa, cp, dlen); |
798 | cp += dlen; | |
984263bc | 799 | } |
984263bc MD |
800 | } |
801 | ||
590b8cd4 JH |
802 | /* |
803 | * Build a routing message in a mbuf chain. | |
804 | * Copy the addresses in the rtinfo->rti_info[] sockaddr array | |
805 | * to the end of the mbuf after the message header. | |
806 | * | |
807 | * Set the rtinfo->rti_addrs bitmask of addresses present in rtinfo->rti_info[]. | |
808 | * This side-effect can be avoided if we reorder the addrs bitmask field in all | |
809 | * the route messages to line up so we can set it here instead of back in the | |
810 | * calling routine. | |
811 | */ | |
812 | static struct mbuf * | |
813 | rt_msg_mbuf(int type, struct rt_addrinfo *rtinfo) | |
984263bc | 814 | { |
590b8cd4 JH |
815 | struct mbuf *m; |
816 | struct rt_msghdr *rtm; | |
817 | int hlen, len; | |
82ed7fc2 | 818 | int i; |
984263bc | 819 | |
590b8cd4 JH |
820 | hlen = rt_msghdrsize(type); |
821 | KASSERT(hlen <= MCLBYTES, ("rt_msg_mbuf: hlen %d doesn't fit", hlen)); | |
984263bc | 822 | |
50503f0f | 823 | m = m_getl(hlen, MB_DONTWAIT, MT_DATA, M_PKTHDR, NULL); |
590b8cd4 JH |
824 | if (m == NULL) |
825 | return (NULL); | |
590b8cd4 JH |
826 | m->m_pkthdr.len = m->m_len = hlen; |
827 | m->m_pkthdr.rcvif = NULL; | |
828 | rtinfo->rti_addrs = 0; | |
829 | len = hlen; | |
984263bc | 830 | for (i = 0; i < RTAX_MAX; i++) { |
82ed7fc2 | 831 | struct sockaddr *sa; |
590b8cd4 | 832 | int dlen; |
984263bc | 833 | |
ef87f48d | 834 | if ((sa = rtinfo->rti_info[i]) == NULL) |
984263bc MD |
835 | continue; |
836 | rtinfo->rti_addrs |= (1 << i); | |
837 | dlen = ROUNDUP(sa->sa_len); | |
590b8cd4 | 838 | m_copyback(m, len, dlen, (caddr_t)sa); /* can grow mbuf chain */ |
984263bc MD |
839 | len += dlen; |
840 | } | |
590b8cd4 JH |
841 | if (m->m_pkthdr.len != len) { /* one of the m_copyback() calls failed */ |
842 | m_freem(m); | |
843 | return (NULL); | |
984263bc | 844 | } |
590b8cd4 JH |
845 | rtm = mtod(m, struct rt_msghdr *); |
846 | bzero(rtm, hlen); | |
847 | rtm->rtm_msglen = len; | |
848 | rtm->rtm_version = RTM_VERSION; | |
849 | rtm->rtm_type = type; | |
850 | return (m); | |
984263bc MD |
851 | } |
852 | ||
853 | /* | |
854 | * This routine is called to generate a message from the routing | |
f23061d4 | 855 | * socket indicating that a redirect has occurred, a routing lookup |
984263bc MD |
856 | * has failed, or that a protocol has detected timeouts to a particular |
857 | * destination. | |
858 | */ | |
859 | void | |
f23061d4 | 860 | rt_missmsg(int type, struct rt_addrinfo *rtinfo, int flags, int error) |
984263bc | 861 | { |
0c3c561c | 862 | struct sockaddr *dst = rtinfo->rti_info[RTAX_DST]; |
82ed7fc2 RG |
863 | struct rt_msghdr *rtm; |
864 | struct mbuf *m; | |
984263bc MD |
865 | |
866 | if (route_cb.any_count == 0) | |
867 | return; | |
590b8cd4 | 868 | m = rt_msg_mbuf(type, rtinfo); |
ef87f48d | 869 | if (m == NULL) |
984263bc MD |
870 | return; |
871 | rtm = mtod(m, struct rt_msghdr *); | |
872 | rtm->rtm_flags = RTF_DONE | flags; | |
873 | rtm->rtm_errno = error; | |
874 | rtm->rtm_addrs = rtinfo->rti_addrs; | |
590b8cd4 | 875 | rts_input(m, familyof(dst)); |
984263bc MD |
876 | } |
877 | ||
f3ed2586 JH |
878 | void |
879 | rt_dstmsg(int type, struct sockaddr *dst, int error) | |
880 | { | |
881 | struct rt_msghdr *rtm; | |
882 | struct rt_addrinfo addrs; | |
883 | struct mbuf *m; | |
884 | ||
885 | if (route_cb.any_count == 0) | |
886 | return; | |
887 | bzero(&addrs, sizeof(struct rt_addrinfo)); | |
888 | addrs.rti_info[RTAX_DST] = dst; | |
590b8cd4 | 889 | m = rt_msg_mbuf(type, &addrs); |
f3ed2586 JH |
890 | if (m == NULL) |
891 | return; | |
892 | rtm = mtod(m, struct rt_msghdr *); | |
893 | rtm->rtm_flags = RTF_DONE; | |
894 | rtm->rtm_errno = error; | |
895 | rtm->rtm_addrs = addrs.rti_addrs; | |
590b8cd4 | 896 | rts_input(m, familyof(dst)); |
f3ed2586 JH |
897 | } |
898 | ||
984263bc MD |
899 | /* |
900 | * This routine is called to generate a message from the routing | |
901 | * socket indicating that the status of a network interface has changed. | |
902 | */ | |
903 | void | |
0c3c561c | 904 | rt_ifmsg(struct ifnet *ifp) |
984263bc | 905 | { |
82ed7fc2 | 906 | struct if_msghdr *ifm; |
984263bc | 907 | struct mbuf *m; |
590b8cd4 | 908 | struct rt_addrinfo rtinfo; |
984263bc MD |
909 | |
910 | if (route_cb.any_count == 0) | |
911 | return; | |
590b8cd4 JH |
912 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
913 | m = rt_msg_mbuf(RTM_IFINFO, &rtinfo); | |
ef87f48d | 914 | if (m == NULL) |
984263bc MD |
915 | return; |
916 | ifm = mtod(m, struct if_msghdr *); | |
917 | ifm->ifm_index = ifp->if_index; | |
9c095379 | 918 | ifm->ifm_flags = ifp->if_flags; |
984263bc | 919 | ifm->ifm_data = ifp->if_data; |
590b8cd4 JH |
920 | ifm->ifm_addrs = 0; |
921 | rts_input(m, 0); | |
984263bc MD |
922 | } |
923 | ||
372316d9 JH |
924 | static void |
925 | rt_ifamsg(int cmd, struct ifaddr *ifa) | |
926 | { | |
927 | struct ifa_msghdr *ifam; | |
590b8cd4 | 928 | struct rt_addrinfo rtinfo; |
372316d9 | 929 | struct mbuf *m; |
372316d9 JH |
930 | struct ifnet *ifp = ifa->ifa_ifp; |
931 | ||
590b8cd4 JH |
932 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
933 | rtinfo.rti_ifaaddr = ifa->ifa_addr; | |
934 | rtinfo.rti_ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; | |
935 | rtinfo.rti_netmask = ifa->ifa_netmask; | |
936 | rtinfo.rti_bcastaddr = ifa->ifa_dstaddr; | |
f23061d4 | 937 | |
590b8cd4 | 938 | m = rt_msg_mbuf(cmd, &rtinfo); |
f23061d4 | 939 | if (m == NULL) |
372316d9 | 940 | return; |
f23061d4 | 941 | |
372316d9 JH |
942 | ifam = mtod(m, struct ifa_msghdr *); |
943 | ifam->ifam_index = ifp->if_index; | |
944 | ifam->ifam_metric = ifa->ifa_metric; | |
945 | ifam->ifam_flags = ifa->ifa_flags; | |
590b8cd4 | 946 | ifam->ifam_addrs = rtinfo.rti_addrs; |
f23061d4 | 947 | |
590b8cd4 | 948 | rts_input(m, familyof(ifa->ifa_addr)); |
372316d9 JH |
949 | } |
950 | ||
f3ed2586 JH |
951 | void |
952 | rt_rtmsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int error) | |
372316d9 JH |
953 | { |
954 | struct rt_msghdr *rtm; | |
590b8cd4 | 955 | struct rt_addrinfo rtinfo; |
372316d9 | 956 | struct mbuf *m; |
f3ed2586 | 957 | struct sockaddr *dst; |
372316d9 JH |
958 | |
959 | if (rt == NULL) | |
960 | return; | |
f23061d4 | 961 | |
590b8cd4 JH |
962 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
963 | rtinfo.rti_dst = dst = rt_key(rt); | |
964 | rtinfo.rti_gateway = rt->rt_gateway; | |
965 | rtinfo.rti_netmask = rt_mask(rt); | |
f3ed2586 | 966 | if (ifp != NULL) |
590b8cd4 JH |
967 | rtinfo.rti_ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; |
968 | rtinfo.rti_ifaaddr = rt->rt_ifa->ifa_addr; | |
f23061d4 | 969 | |
590b8cd4 | 970 | m = rt_msg_mbuf(cmd, &rtinfo); |
f23061d4 | 971 | if (m == NULL) |
372316d9 | 972 | return; |
f23061d4 | 973 | |
372316d9 | 974 | rtm = mtod(m, struct rt_msghdr *); |
f3ed2586 JH |
975 | if (ifp != NULL) |
976 | rtm->rtm_index = ifp->if_index; | |
372316d9 JH |
977 | rtm->rtm_flags |= rt->rt_flags; |
978 | rtm->rtm_errno = error; | |
590b8cd4 | 979 | rtm->rtm_addrs = rtinfo.rti_addrs; |
f23061d4 | 980 | |
590b8cd4 | 981 | rts_input(m, familyof(dst)); |
372316d9 JH |
982 | } |
983 | ||
984263bc MD |
984 | /* |
985 | * This is called to generate messages from the routing socket | |
986 | * indicating a network interface has had addresses associated with it. | |
987 | * if we ever reverse the logic and replace messages TO the routing | |
988 | * socket indicate a request to configure interfaces, then it will | |
989 | * be unnecessary as the routing socket will automatically generate | |
990 | * copies of it. | |
991 | */ | |
992 | void | |
0c3c561c | 993 | rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) |
984263bc | 994 | { |
78812139 EN |
995 | #ifdef SCTP |
996 | /* | |
997 | * notify the SCTP stack | |
998 | * this will only get called when an address is added/deleted | |
999 | * XXX pass the ifaddr struct instead if ifa->ifa_addr... | |
1000 | */ | |
1001 | if (cmd == RTM_ADD) | |
1002 | sctp_add_ip_address(ifa); | |
1003 | else if (cmd == RTM_DELETE) | |
1004 | sctp_delete_ip_address(ifa); | |
1005 | #endif /* SCTP */ | |
1006 | ||
984263bc MD |
1007 | if (route_cb.any_count == 0) |
1008 | return; | |
984263bc | 1009 | |
372316d9 JH |
1010 | if (cmd == RTM_ADD) { |
1011 | rt_ifamsg(RTM_NEWADDR, ifa); | |
f3ed2586 | 1012 | rt_rtmsg(RTM_ADD, rt, ifa->ifa_ifp, error); |
372316d9 JH |
1013 | } else { |
1014 | KASSERT((cmd == RTM_DELETE), ("unknown cmd %d", cmd)); | |
f3ed2586 | 1015 | rt_rtmsg(RTM_DELETE, rt, ifa->ifa_ifp, error); |
372316d9 | 1016 | rt_ifamsg(RTM_DELADDR, ifa); |
984263bc MD |
1017 | } |
1018 | } | |
1019 | ||
1020 | /* | |
1021 | * This is the analogue to the rt_newaddrmsg which performs the same | |
1022 | * function but for multicast group memberhips. This is easier since | |
1023 | * there is no route state to worry about. | |
1024 | */ | |
1025 | void | |
0c3c561c | 1026 | rt_newmaddrmsg(int cmd, struct ifmultiaddr *ifma) |
984263bc | 1027 | { |
590b8cd4 | 1028 | struct rt_addrinfo rtinfo; |
ef87f48d | 1029 | struct mbuf *m = NULL; |
984263bc MD |
1030 | struct ifnet *ifp = ifma->ifma_ifp; |
1031 | struct ifma_msghdr *ifmam; | |
1032 | ||
1033 | if (route_cb.any_count == 0) | |
1034 | return; | |
1035 | ||
590b8cd4 JH |
1036 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
1037 | rtinfo.rti_ifaaddr = ifma->ifma_addr; | |
1782116d | 1038 | if (ifp != NULL && !TAILQ_EMPTY(&ifp->if_addrhead)) |
590b8cd4 | 1039 | rtinfo.rti_ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; |
984263bc MD |
1040 | /* |
1041 | * If a link-layer address is present, present it as a ``gateway'' | |
1042 | * (similarly to how ARP entries, e.g., are presented). | |
1043 | */ | |
590b8cd4 | 1044 | rtinfo.rti_gateway = ifma->ifma_lladdr; |
f23061d4 | 1045 | |
590b8cd4 | 1046 | m = rt_msg_mbuf(cmd, &rtinfo); |
f23061d4 | 1047 | if (m == NULL) |
984263bc | 1048 | return; |
f23061d4 | 1049 | |
984263bc MD |
1050 | ifmam = mtod(m, struct ifma_msghdr *); |
1051 | ifmam->ifmam_index = ifp->if_index; | |
590b8cd4 | 1052 | ifmam->ifmam_addrs = rtinfo.rti_addrs; |
f23061d4 | 1053 | |
590b8cd4 | 1054 | rts_input(m, familyof(ifma->ifma_addr)); |
984263bc MD |
1055 | } |
1056 | ||
1057 | /* | |
1058 | * This is called to generate routing socket messages indicating | |
1059 | * network interface arrival and departure. | |
1060 | */ | |
1061 | void | |
590b8cd4 | 1062 | rt_ifannouncemsg(struct ifnet *ifp, int what) |
984263bc | 1063 | { |
590b8cd4 | 1064 | struct rt_addrinfo addrinfo; |
984263bc | 1065 | struct mbuf *m; |
590b8cd4 | 1066 | struct if_announcemsghdr *ifan; |
984263bc MD |
1067 | |
1068 | if (route_cb.any_count == 0) | |
1069 | return; | |
f23061d4 | 1070 | |
590b8cd4 JH |
1071 | bzero(&addrinfo, sizeof addrinfo); |
1072 | m = rt_msg_mbuf(RTM_IFANNOUNCE, &addrinfo); | |
984263bc MD |
1073 | if (m == NULL) |
1074 | return; | |
f23061d4 | 1075 | |
984263bc MD |
1076 | ifan = mtod(m, struct if_announcemsghdr *); |
1077 | ifan->ifan_index = ifp->if_index; | |
5fe66e68 | 1078 | strlcpy(ifan->ifan_name, ifp->if_xname, sizeof ifan->ifan_name); |
984263bc | 1079 | ifan->ifan_what = what; |
f23061d4 | 1080 | |
590b8cd4 JH |
1081 | rts_input(m, 0); |
1082 | } | |
f23061d4 | 1083 | |
590b8cd4 JH |
1084 | static int |
1085 | resizewalkarg(struct walkarg *w, int len) | |
1086 | { | |
1087 | void *newptr; | |
1088 | ||
1089 | newptr = malloc(len, M_RTABLE, M_INTWAIT | M_NULLOK); | |
1090 | if (newptr == NULL) | |
1091 | return (ENOMEM); | |
1092 | if (w->w_tmem != NULL) | |
1093 | free(w->w_tmem, M_RTABLE); | |
1094 | w->w_tmem = newptr; | |
1095 | w->w_tmemsize = len; | |
1096 | return (0); | |
1097 | } | |
984263bc MD |
1098 | |
1099 | /* | |
1100 | * This is used in dumping the kernel table via sysctl(). | |
1101 | */ | |
1102 | int | |
590b8cd4 | 1103 | sysctl_dumpentry(struct radix_node *rn, void *vw) |
984263bc | 1104 | { |
82ed7fc2 RG |
1105 | struct walkarg *w = vw; |
1106 | struct rtentry *rt = (struct rtentry *)rn; | |
590b8cd4 JH |
1107 | struct rt_addrinfo rtinfo; |
1108 | int error, msglen; | |
984263bc MD |
1109 | |
1110 | if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) | |
1111 | return 0; | |
f23061d4 | 1112 | |
590b8cd4 JH |
1113 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
1114 | rtinfo.rti_dst = rt_key(rt); | |
1115 | rtinfo.rti_gateway = rt->rt_gateway; | |
1116 | rtinfo.rti_netmask = rt_mask(rt); | |
1117 | rtinfo.rti_genmask = rt->rt_genmask; | |
ef87f48d | 1118 | if (rt->rt_ifp != NULL) { |
590b8cd4 | 1119 | rtinfo.rti_ifpaddr = |
ef87f48d | 1120 | TAILQ_FIRST(&rt->rt_ifp->if_addrhead)->ifa_addr; |
590b8cd4 | 1121 | rtinfo.rti_ifaaddr = rt->rt_ifa->ifa_addr; |
984263bc | 1122 | if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) |
590b8cd4 | 1123 | rtinfo.rti_bcastaddr = rt->rt_ifa->ifa_dstaddr; |
984263bc | 1124 | } |
590b8cd4 JH |
1125 | msglen = rt_msgsize(RTM_GET, &rtinfo); |
1126 | if (w->w_tmemsize < msglen && resizewalkarg(w, msglen) != 0) | |
1127 | return (ENOMEM); | |
1128 | rt_msg_buffer(RTM_GET, &rtinfo, w->w_tmem, msglen); | |
1129 | if (w->w_req != NULL) { | |
8e63efec | 1130 | struct rt_msghdr *rtm = w->w_tmem; |
984263bc MD |
1131 | |
1132 | rtm->rtm_flags = rt->rt_flags; | |
1133 | rtm->rtm_use = rt->rt_use; | |
1134 | rtm->rtm_rmx = rt->rt_rmx; | |
1135 | rtm->rtm_index = rt->rt_ifp->if_index; | |
1136 | rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0; | |
590b8cd4 JH |
1137 | rtm->rtm_addrs = rtinfo.rti_addrs; |
1138 | error = SYSCTL_OUT(w->w_req, rtm, msglen); | |
984263bc MD |
1139 | return (error); |
1140 | } | |
590b8cd4 | 1141 | return (0); |
984263bc MD |
1142 | } |
1143 | ||
590b8cd4 JH |
1144 | static int |
1145 | sysctl_iflist(int af, struct walkarg *w) | |
984263bc | 1146 | { |
82ed7fc2 RG |
1147 | struct ifnet *ifp; |
1148 | struct ifaddr *ifa; | |
590b8cd4 JH |
1149 | struct rt_addrinfo rtinfo; |
1150 | int msglen, error; | |
984263bc | 1151 | |
590b8cd4 | 1152 | bzero(&rtinfo, sizeof(struct rt_addrinfo)); |
984263bc MD |
1153 | TAILQ_FOREACH(ifp, &ifnet, if_link) { |
1154 | if (w->w_arg && w->w_arg != ifp->if_index) | |
1155 | continue; | |
1156 | ifa = TAILQ_FIRST(&ifp->if_addrhead); | |
590b8cd4 JH |
1157 | rtinfo.rti_ifpaddr = ifa->ifa_addr; |
1158 | msglen = rt_msgsize(RTM_IFINFO, &rtinfo); | |
1159 | if (w->w_tmemsize < msglen && resizewalkarg(w, msglen) != 0) | |
1160 | return (ENOMEM); | |
1161 | rt_msg_buffer(RTM_IFINFO, &rtinfo, w->w_tmem, msglen); | |
1162 | rtinfo.rti_ifpaddr = NULL; | |
ef87f48d | 1163 | if (w->w_req != NULL && w->w_tmem != NULL) { |
8e63efec | 1164 | struct if_msghdr *ifm = w->w_tmem; |
984263bc | 1165 | |
984263bc | 1166 | ifm->ifm_index = ifp->if_index; |
9c095379 | 1167 | ifm->ifm_flags = ifp->if_flags; |
984263bc | 1168 | ifm->ifm_data = ifp->if_data; |
590b8cd4 JH |
1169 | ifm->ifm_addrs = rtinfo.rti_addrs; |
1170 | error = SYSCTL_OUT(w->w_req, ifm, msglen); | |
984263bc MD |
1171 | if (error) |
1172 | return (error); | |
1173 | } | |
ef87f48d | 1174 | while ((ifa = TAILQ_NEXT(ifa, ifa_link)) != NULL) { |
984263bc MD |
1175 | if (af && af != ifa->ifa_addr->sa_family) |
1176 | continue; | |
590b8cd4 JH |
1177 | if (curproc->p_ucred->cr_prison && |
1178 | prison_if(curthread, ifa->ifa_addr)) | |
984263bc | 1179 | continue; |
590b8cd4 JH |
1180 | rtinfo.rti_ifaaddr = ifa->ifa_addr; |
1181 | rtinfo.rti_netmask = ifa->ifa_netmask; | |
1182 | rtinfo.rti_bcastaddr = ifa->ifa_dstaddr; | |
1183 | msglen = rt_msgsize(RTM_NEWADDR, &rtinfo); | |
1184 | if (w->w_tmemsize < msglen && | |
1185 | resizewalkarg(w, msglen) != 0) | |
1186 | return (ENOMEM); | |
1187 | rt_msg_buffer(RTM_NEWADDR, &rtinfo, w->w_tmem, msglen); | |
1188 | if (w->w_req != NULL) { | |
8e63efec | 1189 | struct ifa_msghdr *ifam = w->w_tmem; |
984263bc | 1190 | |
984263bc MD |
1191 | ifam->ifam_index = ifa->ifa_ifp->if_index; |
1192 | ifam->ifam_flags = ifa->ifa_flags; | |
1193 | ifam->ifam_metric = ifa->ifa_metric; | |
590b8cd4 JH |
1194 | ifam->ifam_addrs = rtinfo.rti_addrs; |
1195 | error = SYSCTL_OUT(w->w_req, w->w_tmem, msglen); | |
984263bc MD |
1196 | if (error) |
1197 | return (error); | |
1198 | } | |
1199 | } | |
590b8cd4 JH |
1200 | rtinfo.rti_netmask = NULL; |
1201 | rtinfo.rti_ifaaddr = NULL; | |
1202 | rtinfo.rti_bcastaddr = NULL; | |
984263bc MD |
1203 | } |
1204 | return (0); | |
1205 | } | |
1206 | ||
1207 | static int | |
1208 | sysctl_rtsock(SYSCTL_HANDLER_ARGS) | |
1209 | { | |
1210 | int *name = (int *)arg1; | |
1211 | u_int namelen = arg2; | |
82ed7fc2 | 1212 | struct radix_node_head *rnh; |
a49aa710 | 1213 | int i, error = EINVAL; |
ecdefdda | 1214 | int origcpu; |
984263bc MD |
1215 | u_char af; |
1216 | struct walkarg w; | |
1217 | ||
1218 | name ++; | |
1219 | namelen--; | |
1220 | if (req->newptr) | |
1221 | return (EPERM); | |
ecdefdda | 1222 | if (namelen != 3 && namelen != 4) |
984263bc MD |
1223 | return (EINVAL); |
1224 | af = name[0]; | |
5fe66e68 | 1225 | bzero(&w, sizeof w); |
984263bc MD |
1226 | w.w_op = name[1]; |
1227 | w.w_arg = name[2]; | |
1228 | w.w_req = req; | |
1229 | ||
ecdefdda MD |
1230 | /* |
1231 | * Optional third argument specifies cpu, used primarily for | |
1232 | * debugging the route table. | |
1233 | */ | |
1234 | if (namelen == 4) { | |
1235 | if (name[3] < 0 || name[3] >= ncpus) | |
1236 | return (EINVAL); | |
1237 | origcpu = mycpuid; | |
1238 | lwkt_migratecpu(name[3]); | |
1239 | } else { | |
1240 | origcpu = -1; | |
1241 | } | |
a49aa710 | 1242 | crit_enter(); |
984263bc | 1243 | switch (w.w_op) { |
984263bc MD |
1244 | case NET_RT_DUMP: |
1245 | case NET_RT_FLAGS: | |
1246 | for (i = 1; i <= AF_MAX; i++) | |
ecdefdda MD |
1247 | if ((rnh = rt_tables[mycpuid][i]) && |
1248 | (af == 0 || af == i) && | |
984263bc | 1249 | (error = rnh->rnh_walktree(rnh, |
f23061d4 | 1250 | sysctl_dumpentry, &w))) |
984263bc MD |
1251 | break; |
1252 | break; | |
1253 | ||
1254 | case NET_RT_IFLIST: | |
1255 | error = sysctl_iflist(af, &w); | |
1256 | } | |
a49aa710 | 1257 | crit_exit(); |
590b8cd4 | 1258 | if (w.w_tmem != NULL) |
984263bc | 1259 | free(w.w_tmem, M_RTABLE); |
ecdefdda MD |
1260 | if (origcpu >= 0) |
1261 | lwkt_migratecpu(origcpu); | |
984263bc MD |
1262 | return (error); |
1263 | } | |
1264 | ||
1265 | SYSCTL_NODE(_net, PF_ROUTE, routetable, CTLFLAG_RD, sysctl_rtsock, ""); | |
1266 | ||
1267 | /* | |
1268 | * Definitions of protocols supported in the ROUTE domain. | |
1269 | */ | |
1270 | ||
1271 | extern struct domain routedomain; /* or at least forward */ | |
1272 | ||
1273 | static struct protosw routesw[] = { | |
1274 | { SOCK_RAW, &routedomain, 0, PR_ATOMIC|PR_ADDR, | |
1275 | 0, route_output, raw_ctlinput, 0, | |
9eeaa8a9 | 1276 | cpu0_soport, |
984263bc MD |
1277 | raw_init, 0, 0, 0, |
1278 | &route_usrreqs | |
1279 | } | |
1280 | }; | |
1281 | ||
9c70fe43 JH |
1282 | static struct domain routedomain = { |
1283 | PF_ROUTE, "route", NULL, NULL, NULL, | |
1284 | routesw, &routesw[(sizeof routesw)/(sizeof routesw[0])], | |
1285 | }; | |
984263bc MD |
1286 | |
1287 | DOMAIN_SET(route); |