2 * Copyright (c) 1988, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * @(#)rtsock.c 8.5 (Berkeley) 11/2/94
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/domain.h>
44 #include <sys/protosw.h>
47 #include <net/route.h>
48 #include <net/raw_cb.h>
52 struct sockaddr route_dst
= { 2, PF_ROUTE
, };
53 struct sockaddr route_src
= { 2, PF_ROUTE
, };
54 struct sockproto route_proto
= { PF_ROUTE
, };
57 int w_op
, w_arg
, w_given
, w_needed
, w_tmemsize
;
58 caddr_t w_where
, w_tmem
;
62 rt_msg1
__P((int, struct rt_addrinfo
*));
63 static int rt_msg2
__P((int,
64 struct rt_addrinfo
*, caddr_t
, struct walkarg
*));
65 static void rt_xaddrs
__P((caddr_t
, caddr_t
, struct rt_addrinfo
*));
67 /* Sleazy use of local variables throughout file, warning!!!! */
68 #define dst info.rti_info[RTAX_DST]
69 #define gate info.rti_info[RTAX_GATEWAY]
70 #define netmask info.rti_info[RTAX_NETMASK]
71 #define genmask info.rti_info[RTAX_GENMASK]
72 #define ifpaddr info.rti_info[RTAX_IFP]
73 #define ifaaddr info.rti_info[RTAX_IFA]
74 #define brdaddr info.rti_info[RTAX_BRD]
78 route_usrreq(so
, req
, m
, nam
, control
)
79 register struct socket
*so
;
81 struct mbuf
*m
, *nam
, *control
;
83 register int error
= 0;
84 register struct rawcb
*rp
= sotorawcb(so
);
87 if (req
== PRU_ATTACH
) {
88 MALLOC(rp
, struct rawcb
*, sizeof(*rp
), M_PCB
, M_WAITOK
);
89 so
->so_pcb
= (caddr_t
)rp
;
91 bzero(so
->so_pcb
, sizeof(*rp
));
94 if (req
== PRU_DETACH
&& rp
) {
95 int af
= rp
->rcb_proto
.sp_protocol
;
100 else if (af
== AF_ISO
)
101 route_cb
.iso_count
--;
102 route_cb
.any_count
--;
105 error
= raw_usrreq(so
, req
, m
, nam
, control
);
107 if (req
== PRU_ATTACH
&& rp
) {
108 int af
= rp
->rcb_proto
.sp_protocol
;
110 free((caddr_t
)rp
, M_PCB
);
116 else if (af
== AF_NS
)
118 else if (af
== AF_ISO
)
119 route_cb
.iso_count
++;
120 rp
->rcb_faddr
= &route_src
;
121 route_cb
.any_count
++;
123 so
->so_options
|= SO_USELOOPBACK
;
132 register struct mbuf
*m
;
135 register struct rt_msghdr
*rtm
= 0;
136 register struct rtentry
*rt
= 0;
137 struct rtentry
*saved_nrt
= 0;
138 struct radix_node_head
*rnh
;
139 struct rt_addrinfo info
;
141 struct ifnet
*ifp
= 0;
142 struct ifaddr
*ifa
= 0;
144 #define senderr(e) { error = e; goto flush;}
145 if (m
== 0 || ((m
->m_len
< sizeof(long)) &&
146 (m
= m_pullup(m
, sizeof(long))) == 0))
148 if ((m
->m_flags
& M_PKTHDR
) == 0)
149 panic("route_output");
150 len
= m
->m_pkthdr
.len
;
151 if (len
< sizeof(*rtm
) ||
152 len
!= mtod(m
, struct rt_msghdr
*)->rtm_msglen
) {
156 R_Malloc(rtm
, struct rt_msghdr
*, len
);
161 m_copydata(m
, 0, len
, (caddr_t
)rtm
);
162 if (rtm
->rtm_version
!= RTM_VERSION
) {
164 senderr(EPROTONOSUPPORT
);
166 rtm
->rtm_pid
= curproc
->p_pid
;
167 info
.rti_addrs
= rtm
->rtm_addrs
;
168 rt_xaddrs((caddr_t
)(rtm
+ 1), len
+ (caddr_t
)rtm
, &info
);
172 struct radix_node
*t
;
173 t
= rn_addmask((caddr_t
)genmask
, 0, 1);
174 if (t
&& Bcmp(genmask
, t
->rn_key
, *(u_char
*)genmask
) == 0)
175 genmask
= (struct sockaddr
*)(t
->rn_key
);
179 switch (rtm
->rtm_type
) {
184 error
= rtrequest(RTM_ADD
, dst
, gate
, netmask
,
185 rtm
->rtm_flags
, &saved_nrt
);
186 if (error
== 0 && saved_nrt
) {
187 rt_setmetrics(rtm
->rtm_inits
,
188 &rtm
->rtm_rmx
, &saved_nrt
->rt_rmx
);
189 saved_nrt
->rt_refcnt
--;
190 saved_nrt
->rt_genmask
= genmask
;
195 error
= rtrequest(RTM_DELETE
, dst
, gate
, netmask
,
196 rtm
->rtm_flags
, &saved_nrt
);
198 if ((rt
= saved_nrt
))
207 if ((rnh
= rt_tables
[dst
->sa_family
]) == 0) {
208 senderr(EAFNOSUPPORT
);
209 } else if ((rt
= (struct rtentry
*)
210 rnh
->rnh_lookup(dst
, netmask
, rnh
)))
214 switch(rtm
->rtm_type
) {
219 gate
= rt
->rt_gateway
;
220 netmask
= rt_mask(rt
);
221 genmask
= rt
->rt_genmask
;
222 if (rtm
->rtm_addrs
& (RTA_IFP
| RTA_IFA
)) {
225 ifpaddr
= ifp
->if_addrlist
->ifa_addr
;
226 ifaaddr
= rt
->rt_ifa
->ifa_addr
;
227 rtm
->rtm_index
= ifp
->if_index
;
233 len
= rt_msg2(rtm
->rtm_type
, &info
, (caddr_t
)0,
234 (struct walkarg
*)0);
235 if (len
> rtm
->rtm_msglen
) {
236 struct rt_msghdr
*new_rtm
;
237 R_Malloc(new_rtm
, struct rt_msghdr
*, len
);
240 Bcopy(rtm
, new_rtm
, rtm
->rtm_msglen
);
241 Free(rtm
); rtm
= new_rtm
;
243 (void)rt_msg2(rtm
->rtm_type
, &info
, (caddr_t
)rtm
,
244 (struct walkarg
*)0);
245 rtm
->rtm_flags
= rt
->rt_flags
;
246 rtm
->rtm_rmx
= rt
->rt_rmx
;
247 rtm
->rtm_addrs
= info
.rti_addrs
;
251 if (gate
&& rt_setgate(rt
, rt_key(rt
), gate
))
255 * If they tried to change things but didn't specify
256 * the required gateway, then just use the old one.
257 * This can happen if the user tries to change the
258 * flags on the default route without changing the
259 * default gateway. Changing flags still doesn't work.
261 if ((rt
->rt_flags
& RTF_GATEWAY
) && !gate
)
262 gate
= rt
->rt_gateway
;
265 /* new gateway could require new ifaddr, ifp;
266 flags may also be different; ifp may be specified
267 by ll sockaddr when protocol address is ambiguous */
268 if (ifpaddr
&& (ifa
= ifa_ifwithnet(ifpaddr
)) &&
269 (ifp
= ifa
->ifa_ifp
))
270 ifa
= ifaof_ifpforaddr(ifaaddr
? ifaaddr
: gate
,
274 else if ((ifaaddr
&& (ifa
= ifa_ifwithaddr(ifaaddr
))) ||
275 (ifa
= ifa_ifwithroute(rt
->rt_flags
,
283 register struct ifaddr
*oifa
= rt
->rt_ifa
;
286 if (oifa
&& oifa
->ifa_rtrequest
)
287 oifa
->ifa_rtrequest(RTM_DELETE
,
296 rt_setmetrics(rtm
->rtm_inits
, &rtm
->rtm_rmx
,
299 if (rt
->rt_ifa
&& rt
->rt_ifa
->ifa_rtrequest
)
300 rt
->rt_ifa
->ifa_rtrequest(RTM_ADD
, rt
, gate
);
303 rt
->rt_genmask
= genmask
;
308 rt
->rt_rmx
.rmx_locks
&= ~(rtm
->rtm_inits
);
309 rt
->rt_rmx
.rmx_locks
|=
310 (rtm
->rtm_inits
& rtm
->rtm_rmx
.rmx_locks
);
322 rtm
->rtm_errno
= error
;
324 rtm
->rtm_flags
|= RTF_DONE
;
329 register struct rawcb
*rp
= 0;
331 * Check to see if we don't want our own messages.
333 if ((so
->so_options
& SO_USELOOPBACK
) == 0) {
334 if (route_cb
.any_count
<= 1) {
340 /* There is another listener, so construct message */
344 m_copyback(m
, 0, rtm
->rtm_msglen
, (caddr_t
)rtm
);
348 rp
->rcb_proto
.sp_family
= 0; /* Avoid us */
350 route_proto
.sp_protocol
= dst
->sa_family
;
351 raw_input(m
, &route_proto
, &route_src
, &route_dst
);
353 rp
->rcb_proto
.sp_family
= PF_ROUTE
;
359 rt_setmetrics(which
, in
, out
)
361 register struct rt_metrics
*in
, *out
;
363 #define metric(f, e) if (which & (f)) out->e = in->e;
364 metric(RTV_RPIPE
, rmx_recvpipe
);
365 metric(RTV_SPIPE
, rmx_sendpipe
);
366 metric(RTV_SSTHRESH
, rmx_ssthresh
);
367 metric(RTV_RTT
, rmx_rtt
);
368 metric(RTV_RTTVAR
, rmx_rttvar
);
369 metric(RTV_HOPCOUNT
, rmx_hopcount
);
370 metric(RTV_MTU
, rmx_mtu
);
371 metric(RTV_EXPIRE
, rmx_expire
);
376 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
377 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
380 rt_xaddrs(cp
, cplim
, rtinfo
)
381 register caddr_t cp
, cplim
;
382 register struct rt_addrinfo
*rtinfo
;
384 register struct sockaddr
*sa
;
387 bzero(rtinfo
->rti_info
, sizeof(rtinfo
->rti_info
));
388 for (i
= 0; (i
< RTAX_MAX
) && (cp
< cplim
); i
++) {
389 if ((rtinfo
->rti_addrs
& (1 << i
)) == 0)
391 rtinfo
->rti_info
[i
] = sa
= (struct sockaddr
*)cp
;
397 rt_msg1(type
, rtinfo
)
399 register struct rt_addrinfo
*rtinfo
;
401 register struct rt_msghdr
*rtm
;
402 register struct mbuf
*m
;
404 register struct sockaddr
*sa
;
407 m
= m_gethdr(M_DONTWAIT
, MT_DATA
);
414 len
= sizeof(struct ifa_msghdr
);
418 len
= sizeof(struct if_msghdr
);
422 len
= sizeof(struct rt_msghdr
);
426 m
->m_pkthdr
.len
= m
->m_len
= len
;
427 m
->m_pkthdr
.rcvif
= 0;
428 rtm
= mtod(m
, struct rt_msghdr
*);
429 bzero((caddr_t
)rtm
, len
);
430 for (i
= 0; i
< RTAX_MAX
; i
++) {
431 if ((sa
= rtinfo
->rti_info
[i
]) == NULL
)
433 rtinfo
->rti_addrs
|= (1 << i
);
434 dlen
= ROUNDUP(sa
->sa_len
);
435 m_copyback(m
, len
, dlen
, (caddr_t
)sa
);
438 if (m
->m_pkthdr
.len
!= len
) {
442 rtm
->rtm_msglen
= len
;
443 rtm
->rtm_version
= RTM_VERSION
;
444 rtm
->rtm_type
= type
;
449 rt_msg2(type
, rtinfo
, cp
, w
)
451 register struct rt_addrinfo
*rtinfo
;
456 int len
, dlen
, second_time
= 0;
459 rtinfo
->rti_addrs
= 0;
465 len
= sizeof(struct ifa_msghdr
);
469 len
= sizeof(struct if_msghdr
);
473 len
= sizeof(struct rt_msghdr
);
478 for (i
= 0; i
< RTAX_MAX
; i
++) {
479 register struct sockaddr
*sa
;
481 if ((sa
= rtinfo
->rti_info
[i
]) == 0)
483 rtinfo
->rti_addrs
|= (1 << i
);
484 dlen
= ROUNDUP(sa
->sa_len
);
486 bcopy((caddr_t
)sa
, cp
, (unsigned)dlen
);
491 if (cp
== 0 && w
!= NULL
&& !second_time
) {
492 register struct walkarg
*rw
= w
;
495 if (rw
->w_needed
<= 0 && rw
->w_where
) {
496 if (rw
->w_tmemsize
< len
) {
498 free(rw
->w_tmem
, M_RTABLE
);
499 rw
->w_tmem
= (caddr_t
)
500 malloc(len
, M_RTABLE
, M_NOWAIT
);
502 rw
->w_tmemsize
= len
;
513 register struct rt_msghdr
*rtm
= (struct rt_msghdr
*)cp0
;
515 rtm
->rtm_version
= RTM_VERSION
;
516 rtm
->rtm_type
= type
;
517 rtm
->rtm_msglen
= len
;
523 * This routine is called to generate a message from the routing
524 * socket indicating that a redirect has occured, a routing lookup
525 * has failed, or that a protocol has detected timeouts to a particular
529 rt_missmsg(type
, rtinfo
, flags
, error
)
530 int type
, flags
, error
;
531 register struct rt_addrinfo
*rtinfo
;
533 register struct rt_msghdr
*rtm
;
534 register struct mbuf
*m
;
535 struct sockaddr
*sa
= rtinfo
->rti_info
[RTAX_DST
];
537 if (route_cb
.any_count
== 0)
539 m
= rt_msg1(type
, rtinfo
);
542 rtm
= mtod(m
, struct rt_msghdr
*);
543 rtm
->rtm_flags
= RTF_DONE
| flags
;
544 rtm
->rtm_errno
= error
;
545 rtm
->rtm_addrs
= rtinfo
->rti_addrs
;
546 route_proto
.sp_protocol
= sa
? sa
->sa_family
: 0;
547 raw_input(m
, &route_proto
, &route_src
, &route_dst
);
551 * This routine is called to generate a message from the routing
552 * socket indicating that the status of a network interface has changed.
556 register struct ifnet
*ifp
;
558 register struct if_msghdr
*ifm
;
560 struct rt_addrinfo info
;
562 if (route_cb
.any_count
== 0)
564 bzero((caddr_t
)&info
, sizeof(info
));
565 m
= rt_msg1(RTM_IFINFO
, &info
);
568 ifm
= mtod(m
, struct if_msghdr
*);
569 ifm
->ifm_index
= ifp
->if_index
;
570 ifm
->ifm_flags
= (u_short
)ifp
->if_flags
;
571 ifm
->ifm_data
= ifp
->if_data
;
573 route_proto
.sp_protocol
= 0;
574 raw_input(m
, &route_proto
, &route_src
, &route_dst
);
578 * This is called to generate messages from the routing socket
579 * indicating a network interface has had addresses associated with it.
580 * if we ever reverse the logic and replace messages TO the routing
581 * socket indicate a request to configure interfaces, then it will
582 * be unnecessary as the routing socket will automatically generate
586 rt_newaddrmsg(cmd
, ifa
, error
, rt
)
588 register struct ifaddr
*ifa
;
589 register struct rtentry
*rt
;
591 struct rt_addrinfo info
;
592 struct sockaddr
*sa
= 0;
596 struct ifnet
*ifp
= ifa
->ifa_ifp
;
599 if (route_cb
.any_count
== 0)
601 for (pass
= 1; pass
< 3; pass
++) {
602 bzero((caddr_t
)&info
, sizeof(info
));
603 if ((cmd
== RTM_ADD
&& pass
== 1) ||
604 (cmd
== RTM_DELETE
&& pass
== 2)) {
605 register struct ifa_msghdr
*ifam
;
606 int ncmd
= cmd
== RTM_ADD
? RTM_NEWADDR
: RTM_DELADDR
;
608 ifaaddr
= sa
= ifa
->ifa_addr
;
610 ifpaddr
= ifp
->if_addrlist
->ifa_addr
;
612 netmask
= ifa
->ifa_netmask
;
613 brdaddr
= ifa
->ifa_dstaddr
;
614 if ((m
= rt_msg1(ncmd
, &info
)) == NULL
)
616 ifam
= mtod(m
, struct ifa_msghdr
*);
618 ifam
->ifam_index
= ifp
->if_index
;
620 ifam
->ifam_metric
= ifa
->ifa_metric
;
621 ifam
->ifam_flags
= ifa
->ifa_flags
;
622 ifam
->ifam_addrs
= info
.rti_addrs
;
624 if ((cmd
== RTM_ADD
&& pass
== 2) ||
625 (cmd
== RTM_DELETE
&& pass
== 1)) {
626 register struct rt_msghdr
*rtm
;
630 netmask
= rt_mask(rt
);
631 dst
= sa
= rt_key(rt
);
632 gate
= rt
->rt_gateway
;
633 if ((m
= rt_msg1(cmd
, &info
)) == NULL
)
635 rtm
= mtod(m
, struct rt_msghdr
*);
637 rtm
->rtm_index
= ifp
->if_index
;
639 rtm
->rtm_flags
|= rt
->rt_flags
;
640 rtm
->rtm_errno
= error
;
641 rtm
->rtm_addrs
= info
.rti_addrs
;
643 route_proto
.sp_protocol
= sa
? sa
->sa_family
: 0;
644 raw_input(m
, &route_proto
, &route_src
, &route_dst
);
649 * This is used in dumping the kernel table via sysctl().
652 sysctl_dumpentry(rn
, w
)
653 struct radix_node
*rn
;
654 register struct walkarg
*w
;
656 register struct rtentry
*rt
= (struct rtentry
*)rn
;
658 struct rt_addrinfo info
;
660 if (w
->w_op
== NET_RT_FLAGS
&& !(rt
->rt_flags
& w
->w_arg
))
662 bzero((caddr_t
)&info
, sizeof(info
));
664 gate
= rt
->rt_gateway
;
665 netmask
= rt_mask(rt
);
666 genmask
= rt
->rt_genmask
;
667 size
= rt_msg2(RTM_GET
, &info
, 0, w
);
668 if (w
->w_where
&& w
->w_tmem
) {
669 register struct rt_msghdr
*rtm
= (struct rt_msghdr
*)w
->w_tmem
;
671 rtm
->rtm_flags
= rt
->rt_flags
;
672 rtm
->rtm_use
= rt
->rt_use
;
673 rtm
->rtm_rmx
= rt
->rt_rmx
;
674 rtm
->rtm_index
= rt
->rt_ifp
->if_index
;
675 rtm
->rtm_errno
= rtm
->rtm_pid
= rtm
->rtm_seq
= 0;
676 rtm
->rtm_addrs
= info
.rti_addrs
;
677 error
= copyout((caddr_t
)rtm
, w
->w_where
, size
);
689 register struct walkarg
*w
;
691 register struct ifnet
*ifp
;
692 register struct ifaddr
*ifa
;
693 struct rt_addrinfo info
;
696 bzero((caddr_t
)&info
, sizeof(info
));
697 for (ifp
= ifnet
; ifp
; ifp
= ifp
->if_next
) {
698 if (w
->w_arg
&& w
->w_arg
!= ifp
->if_index
)
700 ifa
= ifp
->if_addrlist
;
701 ifpaddr
= ifa
->ifa_addr
;
702 len
= rt_msg2(RTM_IFINFO
, &info
, (caddr_t
)0, w
);
704 if (w
->w_where
&& w
->w_tmem
) {
705 register struct if_msghdr
*ifm
;
707 ifm
= (struct if_msghdr
*)w
->w_tmem
;
708 ifm
->ifm_index
= ifp
->if_index
;
709 ifm
->ifm_flags
= (u_short
)ifp
->if_flags
;
710 ifm
->ifm_data
= ifp
->if_data
;
711 ifm
->ifm_addrs
= info
.rti_addrs
;
712 error
= copyout((caddr_t
)ifm
, w
->w_where
, len
);
718 while ((ifa
= ifa
->ifa_next
) != 0) {
719 if (af
&& af
!= ifa
->ifa_addr
->sa_family
)
721 ifaaddr
= ifa
->ifa_addr
;
722 netmask
= ifa
->ifa_netmask
;
723 brdaddr
= ifa
->ifa_dstaddr
;
724 len
= rt_msg2(RTM_NEWADDR
, &info
, 0, w
);
725 if (w
->w_where
&& w
->w_tmem
) {
726 register struct ifa_msghdr
*ifam
;
728 ifam
= (struct ifa_msghdr
*)w
->w_tmem
;
729 ifam
->ifam_index
= ifa
->ifa_ifp
->if_index
;
730 ifam
->ifam_flags
= ifa
->ifa_flags
;
731 ifam
->ifam_metric
= ifa
->ifa_metric
;
732 ifam
->ifam_addrs
= info
.rti_addrs
;
733 error
= copyout(w
->w_tmem
, w
->w_where
, len
);
740 ifaaddr
= netmask
= brdaddr
= 0;
746 sysctl_rtable(name
, namelen
, where
, given
, new, newlen
)
754 register struct radix_node_head
*rnh
;
755 int i
, s
, error
= EINVAL
;
764 Bzero(&w
, sizeof(w
));
767 w
.w_needed
= 0 - w
.w_given
;
776 for (i
= 1; i
<= AF_MAX
; i
++)
777 if ((rnh
= rt_tables
[i
]) && (af
== 0 || af
== i
) &&
778 (error
= rnh
->rnh_walktree(rnh
,
779 sysctl_dumpentry
, &w
)))
784 error
= sysctl_iflist(af
, &w
);
788 free(w
.w_tmem
, M_RTABLE
);
789 w
.w_needed
+= w
.w_given
;
791 *given
= w
.w_where
- where
;
792 if (*given
< w
.w_needed
)
795 *given
= (11 * w
.w_needed
) / 10;
801 * Definitions of protocols supported in the ROUTE domain.
804 extern struct domain routedomain
; /* or at least forward */
806 struct protosw routesw
[] = {
807 { SOCK_RAW
, &routedomain
, 0, PR_ATOMIC
|PR_ADDR
,
808 raw_input
, route_output
, raw_ctlinput
, 0,
815 struct domain routedomain
=
816 { PF_ROUTE
, "route", route_init
, 0, 0,
817 routesw
, &routesw
[sizeof(routesw
)/sizeof(routesw
[0])] };