]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/uipc_mbufhash.c
Xr make_dev(9) from devfs(5).
[FreeBSD/FreeBSD.git] / sys / kern / uipc_mbufhash.c
1 /*      $OpenBSD: if_trunk.c,v 1.30 2007/01/31 06:20:19 reyk Exp $      */
2
3 /*
4  * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include <sys/cdefs.h>
21 __FBSDID("$FreeBSD$");
22
23 #include "opt_inet.h"
24 #include "opt_inet6.h"
25
26 #include <sys/param.h>
27 #include <sys/mbuf.h>
28 #include <sys/fnv_hash.h>
29
30 #include <net/ethernet.h>
31
32 #if defined(INET) || defined(INET6)
33 #include <netinet/in.h>
34 #endif
35
36 #ifdef INET
37 #include <netinet/ip.h>
38 #endif
39
40 #ifdef INET6
41 #include <netinet/ip6.h>
42 #endif
43
44 static const void *
45 m_ether_tcpip_hash_gethdr(const struct mbuf *m, const u_int off,
46     const u_int len, void *buf)
47 {
48
49         if (m->m_pkthdr.len < (off + len)) {
50                 return (NULL);
51         } else if (m->m_len < (off + len)) {
52                 m_copydata(m, off, len, buf);
53                 return (buf);
54         }
55         return (mtod(m, char *) + off);
56 }
57
58 uint32_t
59 m_ether_tcpip_hash_init(void)
60 {
61         uint32_t seed;
62
63         seed = arc4random();
64         return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT));
65 }
66
67 uint32_t
68 m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m,
69     const uint32_t key)
70 {
71         union {
72 #ifdef INET
73                 struct ip ip;
74 #endif
75 #ifdef INET6
76                 struct ip6_hdr ip6;
77 #endif
78                 struct ether_vlan_header vlan;
79                 uint32_t port;
80         } buf;
81         struct ether_header *eh;
82         const struct ether_vlan_header *vlan;
83 #ifdef INET
84         const struct ip *ip;
85 #endif
86 #ifdef INET6
87         const struct ip6_hdr *ip6;
88 #endif
89         uint32_t p;
90         int off;
91         uint16_t etype;
92
93         p = key;
94         off = sizeof(*eh);
95         if (m->m_len < off)
96                 goto done;
97         eh = mtod(m, struct ether_header *);
98         etype = ntohs(eh->ether_type);
99         if (flags & MBUF_HASHFLAG_L2) {
100                 p = fnv_32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p);
101                 p = fnv_32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
102         }
103         /* Special handling for encapsulating VLAN frames */
104         if ((m->m_flags & M_VLANTAG) && (flags & MBUF_HASHFLAG_L2)) {
105                 p = fnv_32_buf(&m->m_pkthdr.ether_vtag,
106                     sizeof(m->m_pkthdr.ether_vtag), p);
107         } else if (etype == ETHERTYPE_VLAN) {
108                 vlan = m_ether_tcpip_hash_gethdr(m, off, sizeof(*vlan), &buf);
109                 if (vlan == NULL)
110                         goto done;
111
112                 if (flags & MBUF_HASHFLAG_L2)
113                         p = fnv_32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p);
114                 etype = ntohs(vlan->evl_proto);
115                 off += sizeof(*vlan) - sizeof(*eh);
116         }
117         switch (etype) {
118 #ifdef INET
119         case ETHERTYPE_IP:
120                 ip = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip), &buf);
121                 if (ip == NULL)
122                         break;
123                 if (flags & MBUF_HASHFLAG_L3) {
124                         p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p);
125                         p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
126                 }
127                 if (flags & MBUF_HASHFLAG_L4) {
128                         const uint32_t *ports;
129                         int iphlen;
130
131                         switch (ip->ip_p) {
132                         case IPPROTO_TCP:
133                         case IPPROTO_UDP:
134                         case IPPROTO_SCTP:
135                                 iphlen = ip->ip_hl << 2;
136                                 if (iphlen < sizeof(*ip))
137                                         break;
138                                 off += iphlen;
139                                 ports = m_ether_tcpip_hash_gethdr(m,
140                                     off, sizeof(*ports), &buf);
141                                 if (ports == NULL)
142                                         break;
143                                 p = fnv_32_buf(ports, sizeof(*ports), p);
144                                 break;
145                         default:
146                                 break;
147                         }
148                 }
149                 break;
150 #endif
151 #ifdef INET6
152         case ETHERTYPE_IPV6:
153                 ip6 = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip6), &buf);
154                 if (ip6 == NULL)
155                         break;
156                 if (flags & MBUF_HASHFLAG_L3) {
157                         p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
158                         p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
159                 }
160                 if (flags & MBUF_HASHFLAG_L4) {
161                         uint32_t flow;
162
163                         /* IPv6 flow label */
164                         flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
165                         p = fnv_32_buf(&flow, sizeof(flow), p);
166                 }
167                 break;
168 #endif
169         default:
170                 break;
171         }
172 done:
173         return (p);
174 }