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