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