netisr: rename cpu_portfn() to netisr_portfn().
[dragonfly.git] / sys / net / dummynet / ip_dummynet_glue.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
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  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/mbuf.h>
38 #include <sys/msgport.h>
39 #include <sys/socketvar.h>
40 #include <sys/sysctl.h>
41
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <net/route.h>
45 #include <net/ethernet.h>
46 #include <net/netisr.h>
47 #include <net/netmsg2.h>
48
49 #include <netinet/in.h>
50 #include <netinet/in_var.h>
51 #include <netinet/ip.h>
52 #include <netinet/ip_var.h>
53
54 #include <net/dummynet/ip_dummynet.h>
55
56 static void     ip_dn_ether_output(netmsg_t);
57 static void     ip_dn_ether_demux(netmsg_t);
58 static void     ip_dn_ip_input(netmsg_t);
59 static void     ip_dn_ip_output(netmsg_t);
60
61 static void     ip_dn_sockopt_dispatch(netmsg_t);
62 static void     ip_dn_freepkt_dispatch(netmsg_t);
63 static void     ip_dn_dispatch(netmsg_t);
64
65 static void     ip_dn_freepkt(struct dn_pkt *);
66
67 static int      ip_dn_sockopt_flush(struct sockopt *);
68 static int      ip_dn_sockopt_get(struct sockopt *);
69 static int      ip_dn_sockopt_config(struct sockopt *);
70
71 ip_dn_io_t      *ip_dn_io_ptr;
72 int             ip_dn_cpu = 0;
73
74 TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu);
75
76 SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
77 SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, cpu, CTLFLAG_RD,
78            &ip_dn_cpu, 0, "CPU to run dummynet");
79
80 void
81 ip_dn_queue(struct mbuf *m)
82 {
83         struct netmsg_packet *nmp;
84         lwkt_port_t port;
85
86         M_ASSERTPKTHDR(m);
87         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
88                 ("mbuf is not tagged for dummynet!"));
89
90         nmp = &m->m_hdr.mh_netmsg;
91         netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
92                     0, ip_dn_dispatch);
93         nmp->nm_packet = m;
94
95         port = netisr_portfn(ip_dn_cpu);
96         lwkt_sendmsg(port, &nmp->base.lmsg);
97 }
98
99 void
100 ip_dn_packet_free(struct dn_pkt *pkt)
101 {
102         struct netmsg_packet *nmp;
103         struct mbuf *m = pkt->dn_m;
104
105         M_ASSERTPKTHDR(m);
106         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
107                 ("mbuf is not tagged for dummynet!"));
108
109         nmp = &m->m_hdr.mh_netmsg;
110         netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
111                     0, ip_dn_freepkt_dispatch);
112         nmp->nm_packet = m;
113
114         lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
115 }
116
117 void
118 ip_dn_packet_redispatch(struct dn_pkt *pkt)
119 {
120         static const netisr_fn_t dispatches[DN_TO_MAX] = {
121         [DN_TO_IP_OUT]          = ip_dn_ip_output,
122         [DN_TO_IP_IN]           = ip_dn_ip_input,
123         [DN_TO_ETH_DEMUX]       = ip_dn_ether_demux,
124         [DN_TO_ETH_OUT]         = ip_dn_ether_output
125         };
126
127         struct netmsg_packet *nmp;
128         struct mbuf *m;
129         netisr_fn_t dispatch;
130         int dir;
131
132         dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK);
133         KASSERT(dir < DN_TO_MAX,
134                 ("unknown dummynet redispatch dir %d", dir));
135
136         dispatch = dispatches[dir];
137         KASSERT(dispatch != NULL,
138                 ("unsupported dummynet redispatch dir %d", dir));
139
140         m = pkt->dn_m;
141         M_ASSERTPKTHDR(m);
142         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
143                 ("mbuf is not tagged for dummynet!"));
144
145         nmp = &m->m_hdr.mh_netmsg;
146         netmsg_init(&nmp->base, NULL, &netisr_apanic_rport, 0, dispatch);
147         nmp->nm_packet = m;
148
149         lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
150 }
151
152 int
153 ip_dn_sockopt(struct sockopt *sopt)
154 {
155         int error = 0;
156
157         /* Disallow sets in really-really secure mode. */
158         if (sopt->sopt_dir == SOPT_SET) {
159                 if (securelevel >= 3)
160                         return EPERM;
161         }
162
163         switch (sopt->sopt_name) {
164         case IP_DUMMYNET_GET:
165                 error = ip_dn_sockopt_get(sopt);
166                 break;
167
168         case IP_DUMMYNET_FLUSH:
169                 error = ip_dn_sockopt_flush(sopt);
170                 break;
171
172         case IP_DUMMYNET_DEL:
173         case IP_DUMMYNET_CONFIGURE:
174                 error = ip_dn_sockopt_config(sopt);
175                 break;
176
177         default:
178                 kprintf("%s -- unknown option %d\n", __func__, sopt->sopt_name);
179                 error = EINVAL;
180                 break;
181         }
182         return error;
183 }
184
185 static void
186 ip_dn_freepkt(struct dn_pkt *pkt)
187 {
188         struct rtentry *rt = pkt->ro.ro_rt;
189
190         /* Unreference route entry */
191         if (rt != NULL) {
192                 if (rt->rt_refcnt <= 0) {       /* XXX assert? */
193                         kprintf("-- warning, refcnt now %ld, decreasing\n",
194                                 rt->rt_refcnt);
195                 }
196                 RTFREE(rt);
197         }
198
199         /* Unreference packet private data */
200         if (pkt->dn_unref_priv)
201                 pkt->dn_unref_priv(pkt->dn_priv);
202
203         /* Free the parent mbuf, this will free 'pkt' as well */
204         m_freem(pkt->dn_m);
205 }
206
207 static void
208 ip_dn_freepkt_dispatch(netmsg_t nmsg)
209 {
210         struct netmsg_packet *nmp;
211         struct mbuf *m;
212         struct m_tag *mtag;
213         struct dn_pkt *pkt;
214
215         nmp = &nmsg->packet;
216         m = nmp->nm_packet;
217         M_ASSERTPKTHDR(m);
218         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
219                 ("mbuf is not tagged for dummynet!"));
220
221         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
222         KKASSERT(mtag != NULL);
223
224         pkt = m_tag_data(mtag);
225         KASSERT(pkt->cpuid == mycpuid,
226                 ("%s: dummynet packet was delivered to wrong cpu! "
227                  "target cpuid %d, mycpuid %d", __func__,
228                  pkt->cpuid, mycpuid));
229
230         ip_dn_freepkt(pkt);
231 }
232
233 static void
234 ip_dn_dispatch(netmsg_t nmsg)
235 {
236         struct netmsg_packet *nmp;
237         struct mbuf *m;
238         struct m_tag *mtag;
239         struct dn_pkt *pkt;
240
241         KASSERT(ip_dn_cpu == mycpuid,
242                 ("%s: dummynet packet was delivered to wrong cpu! "
243                  "dummynet cpuid %d, mycpuid %d", __func__,
244                  ip_dn_cpu, mycpuid));
245
246         nmp = &nmsg->packet;
247         m = nmp->nm_packet;
248         M_ASSERTPKTHDR(m);
249         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
250                 ("mbuf is not tagged for dummynet!"));
251
252         if (DUMMYNET_LOADED) {
253                 if (ip_dn_io_ptr(m) == 0)
254                         return;
255         }
256
257         /*
258          * ip_dn_io_ptr() failed or dummynet(4) is not loaded
259          */
260         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
261         KKASSERT(mtag != NULL);
262
263         pkt = m_tag_data(mtag);
264         ip_dn_packet_free(pkt);
265 }
266
267 static void
268 ip_dn_ip_output(netmsg_t nmsg)
269 {
270         struct netmsg_packet *nmp;
271         struct mbuf *m;
272         struct m_tag *mtag;
273         struct dn_pkt *pkt;
274         struct rtentry *rt;
275         ip_dn_unref_priv_t unref_priv;
276         void *priv;
277
278         nmp = &nmsg->packet;
279         m = nmp->nm_packet;
280         M_ASSERTPKTHDR(m);
281         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
282                 ("mbuf is not tagged for dummynet!"));
283
284         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
285         KKASSERT(mtag != NULL);
286
287         pkt = m_tag_data(mtag);
288         KASSERT(pkt->cpuid == mycpuid,
289                 ("%s: dummynet packet was delivered to wrong cpu! "
290                  "target cpuid %d, mycpuid %d", __func__,
291                  pkt->cpuid, mycpuid));
292         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT,
293                 ("wrong direction %d, should be %d",
294                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT));
295
296         priv = pkt->dn_priv;
297         unref_priv = pkt->dn_unref_priv;
298         rt = pkt->ro.ro_rt;
299
300         if (rt != NULL && !(rt->rt_flags & RTF_UP)) {
301                 /*
302                  * Recorded rtentry is gone, when the packet
303                  * was on delay line.
304                  */
305                 ip_dn_freepkt(pkt);
306                 return;
307         }
308
309         ip_output(pkt->dn_m, NULL, NULL, pkt->flags, NULL, NULL);
310         /* 'rt' will be freed in ip_output */
311
312         if (unref_priv)
313                 unref_priv(priv);
314 }
315
316 static void
317 ip_dn_ip_input(netmsg_t nmsg)
318 {
319         struct netmsg_packet *nmp;
320         struct mbuf *m;
321         struct m_tag *mtag;
322         struct dn_pkt *pkt;
323         ip_dn_unref_priv_t unref_priv;
324         void *priv;
325
326         nmp = &nmsg->packet;
327         m = nmp->nm_packet;
328         M_ASSERTPKTHDR(m);
329         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
330                 ("mbuf is not tagged for dummynet!"));
331
332         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
333         KKASSERT(mtag != NULL);
334
335         pkt = m_tag_data(mtag);
336         KASSERT(pkt->cpuid == mycpuid,
337                 ("%s: dummynet packet was delivered to wrong cpu! "
338                  "target cpuid %d, mycpuid %d", __func__,
339                  pkt->cpuid, mycpuid));
340         KASSERT(pkt->ro.ro_rt == NULL,
341                 ("route entry is not NULL for ip_input"));
342         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN,
343                 ("wrong direction %d, should be %d",
344                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN));
345
346         priv = pkt->dn_priv;
347         unref_priv = pkt->dn_unref_priv;
348
349         ip_input(m);
350
351         if (unref_priv)
352                 unref_priv(priv);
353 }
354
355 static void
356 ip_dn_ether_demux(netmsg_t nmsg)
357 {
358         struct netmsg_packet *nmp;
359         struct mbuf *m;
360         struct m_tag *mtag;
361         struct dn_pkt *pkt;
362         ip_dn_unref_priv_t unref_priv;
363         void *priv;
364
365         nmp = &nmsg->packet;
366         m = nmp->nm_packet;
367         M_ASSERTPKTHDR(m);
368         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
369                 ("mbuf is not tagged for dummynet!"));
370
371         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
372         KKASSERT(mtag != NULL);
373
374         pkt = m_tag_data(mtag);
375         KASSERT(pkt->cpuid == mycpuid,
376                 ("%s: dummynet packet was delivered to wrong cpu! "
377                  "target cpuid %d, mycpuid %d", __func__,
378                  pkt->cpuid, mycpuid));
379         KASSERT(pkt->ro.ro_rt == NULL,
380                 ("route entry is not NULL for ether_demux"));
381         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
382                 ("wrong direction %d, should be %d",
383                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
384
385         priv = pkt->dn_priv;
386         unref_priv = pkt->dn_unref_priv;
387
388         /*
389          * Make sure that ether header is contiguous
390          */
391         if (m->m_len < ETHER_HDR_LEN &&
392             (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
393                 kprintf("%s: pullup fail, dropping pkt\n", __func__);
394                 goto back;
395         }
396         ether_demux_oncpu(m->m_pkthdr.rcvif, m);
397 back:
398         if (unref_priv)
399                 unref_priv(priv);
400 }
401
402 static void
403 ip_dn_ether_output(netmsg_t nmsg)
404 {
405         struct netmsg_packet *nmp;
406         struct mbuf *m;
407         struct m_tag *mtag;
408         struct dn_pkt *pkt;
409         ip_dn_unref_priv_t unref_priv;
410         void *priv;
411
412         nmp = &nmsg->packet;
413         m = nmp->nm_packet;
414         M_ASSERTPKTHDR(m);
415         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
416                 ("mbuf is not tagged for dummynet!"));
417
418         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
419         KKASSERT(mtag != NULL);
420
421         pkt = m_tag_data(mtag);
422         KASSERT(pkt->cpuid == mycpuid,
423                 ("%s: dummynet packet was delivered to wrong cpu! "
424                  "target cpuid %d, mycpuid %d", __func__,
425                  pkt->cpuid, mycpuid));
426         KASSERT(pkt->ro.ro_rt == NULL,
427                 ("route entry is not NULL for ether_output_frame"));
428         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
429                 ("wrong direction %d, should be %d",
430                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
431
432         priv = pkt->dn_priv;
433         unref_priv = pkt->dn_unref_priv;
434
435         ether_output_frame(pkt->ifp, m);
436
437         if (unref_priv)
438                 unref_priv(priv);
439 }
440
441 static void
442 ip_dn_sockopt_dispatch(netmsg_t nmsg)
443 {
444         lwkt_msg *msg = &nmsg->lmsg;
445         struct dn_sopt *dn_sopt = msg->u.ms_resultp;
446         int error;
447
448         KASSERT(ip_dn_cpu == mycpuid,
449                 ("%s: dummynet sockopt is done on wrong cpu! "
450                  "dummynet cpuid %d, mycpuid %d", __func__,
451                  ip_dn_cpu, mycpuid));
452
453         if (DUMMYNET_LOADED)
454                 error = ip_dn_ctl_ptr(dn_sopt);
455         else
456                 error = ENOPROTOOPT;
457         lwkt_replymsg(msg, error);
458 }
459
460 static int
461 ip_dn_sockopt_flush(struct sockopt *sopt)
462 {
463         struct dn_sopt dn_sopt;
464         struct netmsg_base smsg;
465
466         bzero(&dn_sopt, sizeof(dn_sopt));
467         dn_sopt.dn_sopt_name = sopt->sopt_name;
468
469         netmsg_init(&smsg, NULL, &curthread->td_msgport,
470                     0, ip_dn_sockopt_dispatch);
471         smsg.lmsg.u.ms_resultp = &dn_sopt;
472         lwkt_domsg(netisr_portfn(ip_dn_cpu), &smsg.lmsg, 0);
473
474         return smsg.lmsg.ms_error;
475 }
476
477 static int
478 ip_dn_sockopt_get(struct sockopt *sopt)
479 {
480         struct dn_sopt dn_sopt;
481         struct netmsg_base smsg;
482         int error;
483
484         bzero(&dn_sopt, sizeof(dn_sopt));
485         dn_sopt.dn_sopt_name = sopt->sopt_name;
486
487         netmsg_init(&smsg, NULL, &curthread->td_msgport,
488                     0, ip_dn_sockopt_dispatch);
489         smsg.lmsg.u.ms_resultp = &dn_sopt;
490         lwkt_domsg(netisr_portfn(ip_dn_cpu), &smsg.lmsg, 0);
491
492         error = smsg.lmsg.ms_error;
493         if (error) {
494                 KKASSERT(dn_sopt.dn_sopt_arg == NULL);
495                 KKASSERT(dn_sopt.dn_sopt_arglen == 0);
496                 return error;
497         }
498
499         soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen);
500         kfree(dn_sopt.dn_sopt_arg, M_TEMP);
501         return 0;
502 }
503
504 static int
505 ip_dn_sockopt_config(struct sockopt *sopt)
506 {
507         struct dn_ioc_pipe tmp_ioc_pipe;
508         struct dn_sopt dn_sopt;
509         struct netmsg_base smsg;
510         int error;
511
512         error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe,
513                               sizeof tmp_ioc_pipe);
514         if (error)
515                 return error;
516
517         bzero(&dn_sopt, sizeof(dn_sopt));
518         dn_sopt.dn_sopt_name = sopt->sopt_name;
519         dn_sopt.dn_sopt_arg = &tmp_ioc_pipe;
520         dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe);
521
522         netmsg_init(&smsg, NULL, &curthread->td_msgport,
523                     0, ip_dn_sockopt_dispatch);
524         smsg.lmsg.u.ms_resultp = &dn_sopt;
525         lwkt_domsg(netisr_portfn(ip_dn_cpu), &smsg.lmsg, 0);
526
527         return smsg.lmsg.ms_error;
528 }