Make TCP stats per-cpu.
[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.15 2004/04/07 17:01:25 dillon 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 /*
194  * Map a UDP socket to a protocol processing thread.
195  */
196 lwkt_port_t
197 udp_soport(struct socket *so, struct sockaddr *nam)
198 {
199         struct inpcb *inp;
200
201         /*
202          * The following processing all take place on Protocol Thread 0:
203          *   only bind() and connect() have a non-null nam parameter
204          *   attach() has a null socket parameter
205          *   Fast and slow timeouts pass in two NULLs
206          */
207         if (nam != NULL || so == NULL)
208                 return (&udp_thread[0].td_msgport);
209
210         inp = sotoinpcb(so);
211
212         if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr)))
213                 return (&udp_thread[0].td_msgport);
214
215         /*
216          * Rely on type-stable memory and check in protocol handler
217          * to fix race condition here w/ deallocation of inp.  XXX JH
218          */
219
220         return (&udp_thread[INP_MPORT_HASH(inp->inp_faddr.s_addr,
221             inp->inp_laddr.s_addr, inp->inp_fport, inp->inp_lport)].td_msgport);
222 }
223
224 /*
225  * Map a network address to a processor.
226  */
227 int
228 tcp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
229 {
230         return (INP_MPORT_HASH(faddr, laddr, fport, lport));
231 }
232
233 int
234 udp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
235 {
236         if (IN_MULTICAST(ntohl(laddr)))
237                 return (0);
238         else
239                 return (INP_MPORT_HASH(faddr, laddr, fport, lport));
240 }
241
242 /*
243  * Return LWKT port for cpu.
244  */
245 lwkt_port_t
246 tcp_cport(int cpu)
247 {
248         return (&tcp_thread[cpu].td_msgport);
249 }
250
251 void
252 tcp_thread_init(void)
253 {
254         int cpu;
255
256         for (cpu = 0; cpu < ncpus2; cpu++) {
257                 lwkt_create(netmsg_service_loop, NULL, NULL, 
258                         &tcp_thread[cpu], 0, cpu, "tcp_thread %d", cpu);
259         }
260 }
261
262 void
263 udp_thread_init(void)
264 {
265         int cpu;
266
267         for (cpu = 0; cpu < ncpus2; cpu++) {
268                 lwkt_create(netmsg_service_loop, NULL, NULL,
269                         &udp_thread[cpu], 0, cpu, "udp_thread %d", cpu);
270         }
271 }