db616742645dd7f0c22de3ad5207ebc15f460a84
[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.18 2004/04/10 00:10:42 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 #include <sys/globaldata.h>
18
19 #include <net/if.h>
20 #include <net/netisr.h>
21
22 #include <netinet/in_systm.h>
23 #include <netinet/in.h>
24 #include <netinet/in_var.h>
25 #include <netinet/in_pcb.h>
26 #include <netinet/ip.h>
27 #include <netinet/ip_var.h>
28 #include <netinet/tcp.h>
29 #include <netinet/tcpip.h>
30 #include <netinet/tcp_var.h>
31 #include <netinet/udp.h>
32 #include <netinet/udp_var.h>
33
34 extern struct thread netisr_cpu[];
35
36 static struct thread tcp_thread[MAXCPU];
37 static struct thread udp_thread[MAXCPU];
38
39 static __inline int
40 INP_MPORT_HASH(in_addr_t faddr, in_addr_t laddr,
41                in_port_t fport, in_port_t lport)
42 {
43         /*
44          * Use low order bytes.
45          */
46
47 #if (BYTE_ORDER == LITTLE_ENDIAN)
48         KASSERT(ncpus2 < 256, ("need different hash function"));  /* XXX JH */
49         return (((faddr >> 24) ^ (fport >> 8) ^ (laddr >> 24) ^ (lport >> 8)) &
50                 ncpus2_mask);
51 #else
52         return ((faddr ^ fport ^ laddr ^ lport) & ncpus2_mask);
53 #endif
54 }
55
56 /*
57  * Map a packet to a protocol processing thread.
58  */
59 lwkt_port_t
60 ip_mport(struct mbuf *m)
61 {
62         struct ip *ip;
63         int iphlen;
64         struct tcphdr *th;
65         struct udphdr *uh;
66         int thoff;                              /* TCP data offset */
67         lwkt_port_t port;
68         int cpu;
69
70         if (m->m_pkthdr.len < sizeof(struct ip)) {
71                 ipstat.ips_tooshort++;
72                 return (NULL);
73         }
74
75         if (m->m_len < sizeof(struct ip) &&
76             (m = m_pullup(m, sizeof(struct ip))) == NULL) {
77                 ipstat.ips_toosmall++;
78                 return (NULL);
79         }
80
81         ip = mtod(m, struct ip *);
82
83         /*
84          * XXX generic packet handling defrag on CPU 0 for now.
85          */
86         if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK))
87                 return (&netisr_cpu[0].td_msgport);
88
89         iphlen = ip->ip_hl << 2;
90         if (iphlen < sizeof(struct ip)) {       /* minimum header length */
91                 ipstat.ips_badhlen++;
92                 return (NULL);
93         }
94
95         switch (ip->ip_p) {
96         case IPPROTO_TCP:
97                 if (m->m_len < iphlen + sizeof(struct tcphdr) &&
98                     (m = m_pullup(m, iphlen + sizeof(struct tcphdr))) == NULL) {
99                         tcpstat.tcps_rcvshort++;
100                         return (NULL);
101                 }
102                 th = (struct tcphdr *)((caddr_t)ip + iphlen);
103                 thoff = th->th_off << 2;
104                 if (thoff < sizeof(struct tcphdr) ||
105                     thoff > ntohs(ip->ip_len)) {
106                         tcpstat.tcps_rcvbadoff++;
107                         return (NULL);
108                 }
109                 if (m->m_len < iphlen + thoff) {
110                         m = m_pullup(m, iphlen + thoff);
111                         if (m == NULL) {
112                                 tcpstat.tcps_rcvshort++;
113                                 return (NULL);
114                         }
115                         ip = mtod(m, struct ip *);
116                         th = (struct tcphdr *)((caddr_t)ip + iphlen);
117                 }
118
119                 cpu = INP_MPORT_HASH(ip->ip_src.s_addr, ip->ip_dst.s_addr,
120                     th->th_sport, th->th_dport);
121                 port = &tcp_thread[cpu].td_msgport;
122                 break;
123         case IPPROTO_UDP:
124                 if (m->m_len < iphlen + sizeof(struct udphdr)) {
125                         m = m_pullup(m, iphlen + sizeof(struct udphdr));
126                         if (m == NULL) {
127                                 udpstat.udps_hdrops++;
128                                 return (NULL);
129                         }
130                         ip = mtod(m, struct ip *);
131                 }
132                 uh = (struct udphdr *)((caddr_t)ip + iphlen);
133
134                 if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
135                     in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
136                         cpu = 0;
137                 } else {
138                         cpu = INP_MPORT_HASH(ip->ip_src.s_addr,
139                             ip->ip_dst.s_addr, uh->uh_sport, uh->uh_dport);
140                 }
141                 port = &udp_thread[cpu].td_msgport;
142                 break;
143         default:
144                 if (m->m_len < iphlen && (m = m_pullup(m, iphlen)) == NULL) {
145                         ipstat.ips_badhlen++;
146                         return (NULL);
147                 }
148                 port = &netisr_cpu[0].td_msgport;
149                 break;
150         }
151         KKASSERT(port->mp_putport != NULL);
152
153         return (port);
154 }
155
156 /*
157  * Map a TCP socket to a protocol processing thread.
158  */
159 lwkt_port_t
160 tcp_soport(struct socket *so, struct sockaddr *nam)
161 {
162         struct inpcb *inp;
163
164         /*
165          * The following processing all take place on Protocol Thread 0:
166          *   only bind() and connect() have a non-null nam parameter
167          *   attach() has a null socket parameter
168          *   Fast and slow timeouts pass in two NULLs
169          */
170         if (nam != NULL || so == NULL)
171                 return (&tcp_thread[0].td_msgport);
172
173         /*
174          * Already bound and connected.  For TCP connections, the
175          * (faddr, fport, laddr, lport) association cannot change now.
176          *
177          * Note: T/TCP code needs some reorganization to fit into
178          * this model.  XXX JH
179          */
180         inp = sotoinpcb(so);
181         if (!inp)               /* connection reset by peer */
182                 return (&tcp_thread[0].td_msgport);
183
184         /*
185          * Rely on type-stable memory and check in protocol handler
186          * to fix race condition here w/ deallocation of inp.  XXX JH
187          */
188
189         return (&tcp_thread[INP_MPORT_HASH(inp->inp_faddr.s_addr,
190             inp->inp_laddr.s_addr, inp->inp_fport, inp->inp_lport)].td_msgport);
191 }
192
193 lwkt_port_t
194 tcp_addrport(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
195 {
196         return (&tcp_thread[tcp_addrcpu(faddr, fport,
197                                         laddr, lport)].td_msgport);
198 }
199
200 /*
201  * Map a UDP socket to a protocol processing thread.
202  */
203 lwkt_port_t
204 udp_soport(struct socket *so, struct sockaddr *nam)
205 {
206         struct inpcb *inp;
207
208         /*
209          * The following processing all take place on Protocol Thread 0:
210          *   only bind() and connect() have a non-null nam parameter
211          *   attach() has a null socket parameter
212          *   Fast and slow timeouts pass in two NULLs
213          */
214         if (nam != NULL || so == NULL)
215                 return (&udp_thread[0].td_msgport);
216
217         inp = sotoinpcb(so);
218
219         if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr)))
220                 return (&udp_thread[0].td_msgport);
221
222         /*
223          * Rely on type-stable memory and check in protocol handler
224          * to fix race condition here w/ deallocation of inp.  XXX JH
225          */
226
227         return (&udp_thread[INP_MPORT_HASH(inp->inp_faddr.s_addr,
228             inp->inp_laddr.s_addr, inp->inp_fport, inp->inp_lport)].td_msgport);
229 }
230
231 /*
232  * Map a network address to a processor.
233  */
234 int
235 tcp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
236 {
237         return (INP_MPORT_HASH(faddr, laddr, fport, lport));
238 }
239
240 int
241 udp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
242 {
243         if (IN_MULTICAST(ntohl(laddr)))
244                 return (0);
245         else
246                 return (INP_MPORT_HASH(faddr, laddr, fport, lport));
247 }
248
249 /*
250  * Return LWKT port for cpu.
251  */
252 lwkt_port_t
253 tcp_cport(int cpu)
254 {
255         return (&tcp_thread[cpu].td_msgport);
256 }
257
258 /*
259  * We must construct a custom putport function (which runs in the context
260  * of the message originator)
261  * Our custom putport must check for self-referential messages, which can
262  * occur when the so_upcall routine is called (e.g. nfs).  Self referential
263  * messages are simply executed synchronously.
264  */
265 static int
266 netmsg_put_port(lwkt_port_t port, lwkt_msg_t lmsg)
267 {
268     /*
269      * If it's a synchronous message for the same thread,
270      * execute it directly.
271      */
272     if (!(lmsg->ms_flags & MSGF_ASYNC) && port->mp_td == curthread) {
273         struct netmsg *msg = (struct netmsg *)lmsg;
274
275         msg->nm_handler(msg);
276     } else {
277         lwkt_default_putport(port, lmsg);
278     }
279
280     return (EASYNC);
281 }
282
283 void
284 tcp_thread_init(void)
285 {
286         int cpu;
287
288         for (cpu = 0; cpu < ncpus2; cpu++) {
289                 lwkt_create(netmsg_service_loop, NULL, NULL, 
290                         &tcp_thread[cpu], 0, cpu, "tcp_thread %d", cpu);
291                 tcp_thread[cpu].td_msgport.mp_putport = netmsg_put_port;
292         }
293 }
294
295 void
296 udp_thread_init(void)
297 {
298         int cpu;
299
300         for (cpu = 0; cpu < ncpus2; cpu++) {
301                 lwkt_create(netmsg_service_loop, NULL, NULL,
302                         &udp_thread[cpu], 0, cpu, "udp_thread %d", cpu);
303                 udp_thread[cpu].td_msgport.mp_putport = netmsg_put_port;
304         }
305 }