Dispatch upper-half protocol request handling.
[dragonfly.git] / sys / netinet / ip_demux.c
1 /*
2  * Copyright (c) 2003 Jeffrey Hsu
3  * All rights reserved.
4  *
5  * $DragonFly: src/sys/netinet/ip_demux.c,v 1.6 2004/03/06 01:58:55 hsu Exp $
6  */
7
8 #include "opt_inet.h"
9
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/kernel.h>
13 #include <sys/socket.h>
14 #include <sys/socketvar.h>
15 #include <sys/thread.h>
16 #include <sys/sysctl.h>
17
18 #include <net/if.h>
19 #include <net/netisr.h>
20
21 #include <netinet/in_systm.h>
22 #include <netinet/in.h>
23 #include <netinet/in_var.h>
24 #include <netinet/in_pcb.h>
25 #include <netinet/ip.h>
26 #include <netinet/ip_var.h>
27 #include <netinet/tcp.h>
28 #include <netinet/tcpip.h>
29 #include <netinet/tcp_var.h>
30 #include <netinet/udp.h>
31 #include <netinet/udp_var.h>
32
33 extern struct thread netisr_cpu[];
34
35 static struct thread tcp_thread[MAXCPU];
36 static struct thread udp_thread[MAXCPU];
37
38 /*
39  * XXX when we remove the MP lock changes to this must be master-synchronized
40  */
41 static int      ip_mthread_enable = 0;
42 SYSCTL_INT(_net_inet_ip, OID_AUTO, mthread_enable, CTLFLAG_RW,
43     &ip_mthread_enable, 0, "");
44
45 static __inline int
46 INP_MPORT_HASH(in_addr_t src, in_addr_t dst, in_port_t sport, in_port_t dport)
47 {
48         /*
49          * Use low order bytes.
50          */
51
52 #if (BYTE_ORDER == LITTLE_ENDIAN)
53         KASSERT(ncpus2 < 256, ("need different hash function"));  /* XXX JH */
54         return (((src >> 24) ^ (sport >> 8) ^ (dst >> 24) ^ (dport >> 8)) &
55                 ncpus2_mask);
56 #else
57         return ((src ^ sport ^ dst ^ dport) & ncpus2_mask);
58 #endif
59 }
60
61 /*
62  * Map a packet to a protocol processing thread.
63  */
64 lwkt_port_t
65 ip_mport(struct mbuf *m)
66 {
67         struct ip *ip = mtod(m, struct ip *);
68         int iphlen;
69         struct tcphdr *th;
70         struct udphdr *uh;
71         int off;
72         lwkt_port_t port;
73         int cpu;
74
75         if (ip_mthread_enable == 0)
76                 return (&netisr_cpu[0].td_msgport);
77
78         if (m->m_len < sizeof(struct ip) &&
79             (m = m_pullup(m, sizeof(struct ip))) == NULL) {
80                 ipstat.ips_toosmall++;
81                 return (NULL);
82         }
83
84         /*
85          * XXX generic packet handling defrag on CPU 0 for now.
86          */
87         if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK))
88                 return (&netisr_cpu[0].td_msgport);
89
90         iphlen = ip->ip_hl << 2;
91
92         switch (ip->ip_p) {
93         case IPPROTO_TCP:
94                 if (m->m_len < iphlen + sizeof(struct tcphdr) &&
95                     (m = m_pullup(m, iphlen + sizeof(struct tcphdr))) == NULL) {
96                         tcpstat.tcps_rcvshort++;
97                         return (NULL);
98                 }
99                 th = (struct tcphdr *)((caddr_t)ip + iphlen);
100                 off = th->th_off << 2;
101                 if (off < sizeof(struct tcphdr) || off > ip->ip_len) {
102                         tcpstat.tcps_rcvbadoff++;
103                         return (NULL);
104                 }
105                 if (m->m_len < sizeof(struct ip) + off) {
106                     m = m_pullup(m, sizeof(struct ip) + off);
107                     if (m == NULL) {
108                         tcpstat.tcps_rcvshort++;
109                         return (NULL);
110                     }
111                     ip = mtod(m, struct ip *);
112                     th = (struct tcphdr *)((caddr_t)ip + iphlen);
113                 }
114
115                 cpu = INP_MPORT_HASH(ip->ip_src.s_addr, ip->ip_dst.s_addr,
116                     th->th_sport, th->th_dport);
117                 port = &tcp_thread[cpu].td_msgport;
118                 break;
119         case IPPROTO_UDP:
120                 if (m->m_len < iphlen + sizeof(struct udphdr) &&
121                     (m = m_pullup(m, iphlen + sizeof(struct udphdr))) == NULL) {
122                         udpstat.udps_hdrops++;
123                         return (NULL);
124                 }
125                 uh = (struct udphdr *)((caddr_t)ip + iphlen);
126
127                 if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
128                     in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
129                         cpu = 0;
130                 } else {
131                         cpu = INP_MPORT_HASH(ip->ip_src.s_addr,
132                             ip->ip_dst.s_addr, uh->uh_sport, uh->uh_dport);
133                 }
134                 port = &udp_thread[cpu].td_msgport;
135                 break;
136         default:
137                 port = &netisr_cpu[0].td_msgport;
138                 break;
139         }
140         KKASSERT(port->mp_putport != NULL);
141
142         return (port);
143 }
144
145 /*
146  * Map a TCP socket to a protocol processing thread.
147  */
148 lwkt_port_t
149 tcp_soport(struct socket *so, struct sockaddr *nam)
150 {
151         struct inpcb *inp;
152
153         /*
154          * The following processing all take place on Protocol Thread 0:
155          *   only bind() and connect() have a non-null nam parameter
156          *   attach() has a null socket parameter
157          *   Fast and slow timeouts pass in two NULLs
158          */
159         if (nam != NULL || so == NULL)
160                 return (&tcp_thread[0].td_msgport);
161
162         /*
163          * Already bound and connected.  For TCP connections, the
164          * (faddr, fport, laddr, lport) association cannot change now.
165          *
166          * Note: T/TCP code needs some reorganization to fit into
167          * this model.  XXX JH
168          */
169         inp = sotoinpcb(so);
170         if (!inp)               /* connection reset by peer */
171                 return (&tcp_thread[0].td_msgport);
172
173         /*
174          * Rely on type-stable memory and check in protocol handler
175          * to fix race condition here w/ deallocation of inp.  XXX JH
176          */
177
178         return (&tcp_thread[INP_MPORT_HASH(inp->inp_laddr.s_addr,
179             inp->inp_faddr.s_addr, inp->inp_lport,
180             inp->inp_fport)].td_msgport);
181 }
182
183 /*
184  * Map a UDP socket to a protocol processing thread.
185  */
186 lwkt_port_t
187 udp_soport(struct socket *so, struct sockaddr *nam)
188 {
189         struct inpcb *inp;
190
191         /*
192          * The following processing all take place on Protocol Thread 0:
193          *   only bind() and connect() have a non-null nam parameter
194          *   attach() has a null socket parameter
195          *   Fast and slow timeouts pass in two NULLs
196          */
197         if (nam != NULL || so == NULL)
198                 return (&udp_thread[0].td_msgport);
199
200         inp = sotoinpcb(so);
201
202         if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr)))
203                 return (&udp_thread[0].td_msgport);
204
205         /*
206          * Rely on type-stable memory and check in protocol handler
207          * to fix race condition here w/ deallocation of inp.  XXX JH
208          */
209
210         return (&udp_thread[INP_MPORT_HASH(inp->inp_laddr.s_addr,
211             inp->inp_faddr.s_addr, inp->inp_lport,
212             inp->inp_fport)].td_msgport);
213 }
214
215 void
216 tcp_thread_init(void)
217 {
218         int cpu;
219
220         for (cpu = 0; cpu < ncpus2; cpu++) {
221                 lwkt_create(netmsg_service_loop, NULL, NULL, 
222                         &tcp_thread[cpu], 0, cpu, "tcp_thread %d", cpu);
223         }
224 }
225
226 void
227 udp_thread_init(void)
228 {
229         int cpu;
230
231         for (cpu = 0; cpu < ncpus2; cpu++) {
232                 lwkt_create(netmsg_service_loop, NULL, NULL,
233                         &udp_thread[cpu], 0, cpu, "udp_thread %d", cpu);
234         }
235 }