Merge from vendor branch BZIP:
[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  * $DragonFly: src/sys/net/dummynet/ip_dummynet_glue.c,v 1.4 2007/11/18 13:00:28 sephe Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/mbuf.h>
40 #include <sys/msgport.h>
41 #include <sys/socketvar.h>
42 #include <sys/sysctl.h>
43
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/route.h>
47 #include <net/ethernet.h>
48 #include <net/netisr.h>
49 #include <net/netmsg2.h>
50
51 #include <netinet/in.h>
52 #include <netinet/in_var.h>
53 #include <netinet/ip.h>
54 #include <netinet/ip_var.h>
55
56 #include <net/dummynet/ip_dummynet.h>
57
58 static void     ip_dn_ether_output(struct netmsg *);
59 static void     ip_dn_ether_demux(struct netmsg *);
60 static void     ip_dn_ip_input(struct netmsg *);
61 static void     ip_dn_ip_output(struct netmsg *);
62
63 static void     ip_dn_sockopt_dispatch(struct netmsg *);
64 static void     ip_dn_freepkt_dispatch(struct netmsg *);
65 static void     ip_dn_dispatch(struct netmsg *);
66
67 static void     ip_dn_freepkt(struct dn_pkt *);
68
69 static int      ip_dn_sockopt_flush(struct sockopt *);
70 static int      ip_dn_sockopt_get(struct sockopt *);
71 static int      ip_dn_sockopt_config(struct sockopt *);
72
73 ip_dn_io_t      *ip_dn_io_ptr;
74 int             ip_dn_cpu = 0;
75
76 TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu);
77
78 SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
79 SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, cpu, CTLFLAG_RD,
80            &ip_dn_cpu, 0, "CPU to run dummynet");
81
82 void
83 ip_dn_queue(struct mbuf *m)
84 {
85         struct netmsg_packet *nmp;
86         lwkt_port_t port;
87
88         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
89         M_ASSERTPKTHDR(m);
90         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
91                 ("mbuf is not tagged for dummynet!\n"));
92
93         nmp = &m->m_hdr.mh_netmsg;
94         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0,
95                     ip_dn_dispatch);
96         nmp->nm_packet = m;
97
98         port = cpu_portfn(ip_dn_cpu);
99         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
100 }
101
102 void
103 ip_dn_packet_free(struct dn_pkt *pkt)
104 {
105         struct netmsg_packet *nmp;
106         lwkt_port_t port;
107         struct mbuf *m = pkt->dn_m;
108
109         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
110         M_ASSERTPKTHDR(m);
111         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
112                 ("mbuf is not tagged for dummynet!\n"));
113
114         if (pkt->cpuid == mycpuid) {
115                 ip_dn_freepkt(pkt);
116                 return;
117         }
118
119         nmp = &m->m_hdr.mh_netmsg;
120         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0,
121                     ip_dn_freepkt_dispatch);
122         nmp->nm_packet = m;
123
124         port = cpu_portfn(pkt->cpuid);
125         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
126 }
127
128 void
129 ip_dn_packet_redispatch(struct dn_pkt *pkt)
130 {
131         static const netisr_fn_t dispatches[DN_TO_MAX] = {
132         [DN_TO_IP_OUT]          = ip_dn_ip_output,
133         [DN_TO_IP_IN]           = ip_dn_ip_input,
134         [DN_TO_ETH_DEMUX]       = ip_dn_ether_demux,
135         [DN_TO_ETH_OUT]         = ip_dn_ether_output
136         };
137
138         struct netmsg_packet *nmp;
139         struct mbuf *m;
140         netisr_fn_t dispatch;
141         lwkt_port_t port;
142         int dir;
143
144         dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK);
145         KASSERT(dir < DN_TO_MAX,
146                 ("unknown dummynet redispatch dir %d\n", dir));
147
148         dispatch = dispatches[dir];
149         KASSERT(dispatch != NULL,
150                 ("unsupported dummynet redispatch dir %d\n", dir));
151
152         m = pkt->dn_m;
153         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
154         M_ASSERTPKTHDR(m);
155         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
156                 ("mbuf is not tagged for dummynet!\n"));
157
158         nmp = &m->m_hdr.mh_netmsg;
159         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0, dispatch);
160         nmp->nm_packet = m;
161
162         port = cpu_portfn(pkt->cpuid);
163         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
164 }
165
166 int
167 ip_dn_sockopt(struct sockopt *sopt)
168 {
169         int error = 0;
170
171         /* Disallow sets in really-really secure mode. */
172         if (sopt->sopt_dir == SOPT_SET) {
173                 if (securelevel >= 3)
174                         return EPERM;
175         }
176
177         switch (sopt->sopt_name) {
178         case IP_DUMMYNET_GET:
179                 error = ip_dn_sockopt_get(sopt);
180                 break;
181
182         case IP_DUMMYNET_FLUSH:
183                 error = ip_dn_sockopt_flush(sopt);
184                 break;
185
186         case IP_DUMMYNET_DEL:
187         case IP_DUMMYNET_CONFIGURE:
188                 error = ip_dn_sockopt_config(sopt);
189                 break;
190
191         default:
192                 kprintf("%s -- unknown option %d\n", __func__, sopt->sopt_name);
193                 error = EINVAL;
194                 break;
195         }
196         return error;
197 }
198
199 static void
200 ip_dn_freepkt(struct dn_pkt *pkt)
201 {
202         struct rtentry *rt = pkt->ro.ro_rt;
203
204         /* Unreference route entry */
205         if (rt != NULL) {
206                 if (rt->rt_refcnt <= 0) {       /* XXX assert? */
207                         kprintf("-- warning, refcnt now %ld, decreasing\n",
208                                 rt->rt_refcnt);
209                 }
210                 RTFREE(rt);
211         }
212
213         /* Unreference packet private data */
214         if (pkt->dn_unref_priv)
215                 pkt->dn_unref_priv(pkt->dn_priv);
216
217         /* Free the parent mbuf, this will free 'pkt' as well */
218         m_freem(pkt->dn_m);
219 }
220
221 static void
222 ip_dn_freepkt_dispatch(struct netmsg *nmsg)
223 {
224         struct netmsg_packet *nmp;
225         struct mbuf *m;
226         struct m_tag *mtag;
227         struct dn_pkt *pkt;
228
229         nmp = (struct netmsg_packet *)nmsg;
230         m = nmp->nm_packet;
231         M_ASSERTPKTHDR(m);
232         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
233                 ("mbuf is not tagged for dummynet!\n"));
234
235         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
236         KKASSERT(mtag != NULL);
237
238         pkt = m_tag_data(mtag);
239         KASSERT(pkt->cpuid == mycpuid,
240                 ("%s: dummynet packet was delivered to wrong cpu! "
241                  "target cpuid %d, mycpuid %d\n", __func__,
242                  pkt->cpuid, mycpuid));
243
244         ip_dn_freepkt(pkt);
245 }
246
247 static void
248 ip_dn_dispatch(struct netmsg *nmsg)
249 {
250         struct netmsg_packet *nmp;
251         struct mbuf *m;
252         struct m_tag *mtag;
253         struct dn_pkt *pkt;
254
255         KASSERT(ip_dn_cpu == mycpuid,
256                 ("%s: dummynet packet was delivered to wrong cpu! "
257                  "dummynet cpuid %d, mycpuid %d\n", __func__,
258                  ip_dn_cpu, mycpuid));
259
260         nmp = (struct netmsg_packet *)nmsg;
261         m = nmp->nm_packet;
262         M_ASSERTPKTHDR(m);
263         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
264                 ("mbuf is not tagged for dummynet!\n"));
265
266         if (DUMMYNET_LOADED) {
267                 if (ip_dn_io_ptr(m) == 0)
268                         return;
269         }
270
271         /*
272          * ip_dn_io_ptr() failed or dummynet(4) is not loaded
273          */
274         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
275         KKASSERT(mtag != NULL);
276
277         pkt = m_tag_data(mtag);
278         ip_dn_packet_free(pkt);
279 }
280
281 static void
282 ip_dn_ip_output(struct netmsg *nmsg)
283 {
284         struct netmsg_packet *nmp;
285         struct mbuf *m;
286         struct m_tag *mtag;
287         struct dn_pkt *pkt;
288         struct rtentry *rt;
289         ip_dn_unref_priv_t unref_priv;
290         void *priv;
291
292         nmp = (struct netmsg_packet *)nmsg;
293         m = nmp->nm_packet;
294         M_ASSERTPKTHDR(m);
295         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
296                 ("mbuf is not tagged for dummynet!\n"));
297
298         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
299         KKASSERT(mtag != NULL);
300
301         pkt = m_tag_data(mtag);
302         KASSERT(pkt->cpuid == mycpuid,
303                 ("%s: dummynet packet was delivered to wrong cpu! "
304                  "target cpuid %d, mycpuid %d\n", __func__,
305                  pkt->cpuid, mycpuid));
306         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT,
307                 ("wrong direction %d, should be %d\n",
308                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT));
309
310         priv = pkt->dn_priv;
311         unref_priv = pkt->dn_unref_priv;
312         rt = pkt->ro.ro_rt;
313
314         if (rt != NULL && !(rt->rt_flags & RTF_UP)) {
315                 /*
316                  * Recorded rtentry is gone, when the packet
317                  * was on delay line.
318                  */
319                 ip_dn_freepkt(pkt);
320                 return;
321         }
322
323         ip_output(pkt->dn_m, NULL, NULL, 0, NULL, NULL);
324
325         if (rt != NULL) {
326                 if (rt->rt_refcnt <= 0) {       /* XXX assert? */
327                         kprintf("-- warning, refcnt now %ld, decreasing\n",
328                                 rt->rt_refcnt);
329                 }
330                 RTFREE(rt);
331         }
332         if (unref_priv)
333                 unref_priv(priv);
334 }
335
336 static void
337 ip_dn_ip_input(struct netmsg *nmsg)
338 {
339         struct netmsg_packet *nmp;
340         struct mbuf *m;
341         struct m_tag *mtag;
342         struct dn_pkt *pkt;
343         ip_dn_unref_priv_t unref_priv;
344         void *priv;
345
346         nmp = (struct netmsg_packet *)nmsg;
347         m = nmp->nm_packet;
348         M_ASSERTPKTHDR(m);
349         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
350                 ("mbuf is not tagged for dummynet!\n"));
351
352         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
353         KKASSERT(mtag != NULL);
354
355         pkt = m_tag_data(mtag);
356         KASSERT(pkt->cpuid == mycpuid,
357                 ("%s: dummynet packet was delivered to wrong cpu! "
358                  "target cpuid %d, mycpuid %d\n", __func__,
359                  pkt->cpuid, mycpuid));
360         KASSERT(pkt->ro.ro_rt == NULL,
361                 ("route entry is not NULL for ip_input\n"));
362         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN,
363                 ("wrong direction %d, should be %d\n",
364                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN));
365
366         priv = pkt->dn_priv;
367         unref_priv = pkt->dn_unref_priv;
368
369         ip_input(m);
370
371         if (unref_priv)
372                 unref_priv(priv);
373 }
374
375 static void
376 ip_dn_ether_demux(struct netmsg *nmsg)
377 {
378         struct netmsg_packet *nmp;
379         struct mbuf *m;
380         struct m_tag *mtag;
381         struct dn_pkt *pkt;
382         struct ether_header *eh;
383         ip_dn_unref_priv_t unref_priv;
384         void *priv;
385
386         nmp = (struct netmsg_packet *)nmsg;
387         m = nmp->nm_packet;
388         M_ASSERTPKTHDR(m);
389         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
390                 ("mbuf is not tagged for dummynet!\n"));
391
392         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
393         KKASSERT(mtag != NULL);
394
395         pkt = m_tag_data(mtag);
396         KASSERT(pkt->cpuid == mycpuid,
397                 ("%s: dummynet packet was delivered to wrong cpu! "
398                  "target cpuid %d, mycpuid %d\n", __func__,
399                  pkt->cpuid, mycpuid));
400         KASSERT(pkt->ro.ro_rt == NULL,
401                 ("route entry is not NULL for ether_demux\n"));
402         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
403                 ("wrong direction %d, should be %d\n",
404                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
405
406         priv = pkt->dn_priv;
407         unref_priv = pkt->dn_unref_priv;
408
409         if (m->m_len < ETHER_HDR_LEN &&
410             (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
411                 kprintf("%s: pullup fail, dropping pkt\n", __func__);
412                 goto back;
413         }
414
415         /*
416          * Same as ether_input, make eh be a pointer into the mbuf
417          */
418         eh = mtod(m, struct ether_header *);
419         m_adj(m, ETHER_HDR_LEN);
420         ether_demux(NULL, eh, m);
421 back:
422         if (unref_priv)
423                 unref_priv(priv);
424 }
425
426 static void
427 ip_dn_ether_output(struct netmsg *nmsg)
428 {
429         struct netmsg_packet *nmp;
430         struct mbuf *m;
431         struct m_tag *mtag;
432         struct dn_pkt *pkt;
433         ip_dn_unref_priv_t unref_priv;
434         void *priv;
435
436         nmp = (struct netmsg_packet *)nmsg;
437         m = nmp->nm_packet;
438         M_ASSERTPKTHDR(m);
439         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
440                 ("mbuf is not tagged for dummynet!\n"));
441
442         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
443         KKASSERT(mtag != NULL);
444
445         pkt = m_tag_data(mtag);
446         KASSERT(pkt->cpuid == mycpuid,
447                 ("%s: dummynet packet was delivered to wrong cpu! "
448                  "target cpuid %d, mycpuid %d\n", __func__,
449                  pkt->cpuid, mycpuid));
450         KASSERT(pkt->ro.ro_rt == NULL,
451                 ("route entry is not NULL for ether_output_frame\n"));
452         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
453                 ("wrong direction %d, should be %d\n",
454                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
455
456         priv = pkt->dn_priv;
457         unref_priv = pkt->dn_unref_priv;
458
459         ether_output_frame(pkt->ifp, m);
460
461         if (unref_priv)
462                 unref_priv(priv);
463 }
464
465 static void
466 ip_dn_sockopt_dispatch(struct netmsg *nmsg)
467 {
468         lwkt_msg *msg = &nmsg->nm_lmsg;
469         struct dn_sopt *dn_sopt = msg->u.ms_resultp;
470         int error;
471
472         KASSERT(ip_dn_cpu == mycpuid,
473                 ("%s: dummynet sockopt is done on wrong cpu! "
474                  "dummynet cpuid %d, mycpuid %d\n", __func__,
475                  ip_dn_cpu, mycpuid));
476
477         if (DUMMYNET_LOADED)
478                 error = ip_dn_ctl_ptr(dn_sopt);
479         else
480                 error = ENOPROTOOPT;
481         lwkt_replymsg(msg, error);
482 }
483
484 static int
485 ip_dn_sockopt_flush(struct sockopt *sopt)
486 {
487         struct dn_sopt dn_sopt;
488         struct netmsg smsg;
489
490         bzero(&dn_sopt, sizeof(dn_sopt));
491         dn_sopt.dn_sopt_name = sopt->sopt_name;
492
493         netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch);
494         smsg.nm_lmsg.u.ms_resultp = &dn_sopt;
495         lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0);
496
497         return smsg.nm_lmsg.ms_error;
498 }
499
500 static int
501 ip_dn_sockopt_get(struct sockopt *sopt)
502 {
503         struct dn_sopt dn_sopt;
504         struct netmsg smsg;
505         int error;
506
507         bzero(&dn_sopt, sizeof(dn_sopt));
508         dn_sopt.dn_sopt_name = sopt->sopt_name;
509
510         netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch);
511         smsg.nm_lmsg.u.ms_resultp = &dn_sopt;
512         lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0);
513
514         error = smsg.nm_lmsg.ms_error;
515         if (error) {
516                 KKASSERT(dn_sopt.dn_sopt_arg == NULL);
517                 KKASSERT(dn_sopt.dn_sopt_arglen == 0);
518                 return error;
519         }
520
521         error = sooptcopyout(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen);
522         kfree(dn_sopt.dn_sopt_arg, M_TEMP);
523         return error;
524 }
525
526 static int
527 ip_dn_sockopt_config(struct sockopt *sopt)
528 {
529         struct dn_ioc_pipe tmp_ioc_pipe;
530         struct dn_sopt dn_sopt;
531         struct netmsg smsg;
532         int error;
533
534         error = sooptcopyin(sopt, &tmp_ioc_pipe, sizeof(tmp_ioc_pipe),
535                             sizeof(tmp_ioc_pipe));
536         if (error)
537                 return error;
538
539         bzero(&dn_sopt, sizeof(dn_sopt));
540         dn_sopt.dn_sopt_name = sopt->sopt_name;
541         dn_sopt.dn_sopt_arg = &tmp_ioc_pipe;
542         dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe);
543
544         netmsg_init(&smsg, &curthread->td_msgport, 0, ip_dn_sockopt_dispatch);
545         smsg.nm_lmsg.u.ms_resultp = &dn_sopt;
546         lwkt_domsg(cpu_portfn(ip_dn_cpu), &smsg.nm_lmsg, 0);
547
548         return smsg.nm_lmsg.ms_error;
549 }