1d7373c82SJulian Elischer /*-
2d7373c82SJulian Elischer * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru>
3d7373c82SJulian Elischer * All rights reserved.
4d7373c82SJulian Elischer *
5d7373c82SJulian Elischer * Redistribution and use in source and binary forms, with or without
6d7373c82SJulian Elischer * modification, are permitted provided that the following conditions
7d7373c82SJulian Elischer * are met:
8d7373c82SJulian Elischer * 1. Redistributions of source code must retain the above copyright
9d7373c82SJulian Elischer * notice, this list of conditions and the following disclaimer.
10d7373c82SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright
11d7373c82SJulian Elischer * notice, this list of conditions and the following disclaimer in the
12d7373c82SJulian Elischer * documentation and/or other materials provided with the distribution.
13d7373c82SJulian Elischer *
14d7373c82SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d7373c82SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d7373c82SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d7373c82SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d7373c82SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d7373c82SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d7373c82SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d7373c82SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d7373c82SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d7373c82SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d7373c82SJulian Elischer * SUCH DAMAGE.
25d7373c82SJulian Elischer *
26d7373c82SJulian Elischer */
27d7373c82SJulian Elischer
28d7373c82SJulian Elischer #include <sys/cdefs.h>
29d7373c82SJulian Elischer #include "opt_inet.h"
30d7373c82SJulian Elischer #include "opt_inet6.h"
31d7373c82SJulian Elischer
32d7373c82SJulian Elischer #include <sys/param.h>
33d7373c82SJulian Elischer #include <sys/systm.h>
34d7373c82SJulian Elischer #include <sys/kernel.h>
35d7373c82SJulian Elischer #include <sys/endian.h>
36d7373c82SJulian Elischer #include <sys/malloc.h>
37d7373c82SJulian Elischer #include <sys/mbuf.h>
38d7373c82SJulian Elischer #include <sys/socket.h>
39d7373c82SJulian Elischer
40d7373c82SJulian Elischer #include <net/bpf.h>
41d7373c82SJulian Elischer #include <net/ethernet.h>
42d7373c82SJulian Elischer #include <net/if.h>
43d7373c82SJulian Elischer #include <net/if_vlan_var.h>
44d7373c82SJulian Elischer
45d7373c82SJulian Elischer #include <netinet/in.h>
46d7373c82SJulian Elischer #include <netinet/ip.h>
47d7373c82SJulian Elischer #include <netinet/ip6.h>
48d7373c82SJulian Elischer #include <netinet/tcp.h>
49d7373c82SJulian Elischer #include <netinet/udp.h>
50d7373c82SJulian Elischer #include <machine/in_cksum.h>
51d7373c82SJulian Elischer
52d7373c82SJulian Elischer #include <netgraph/ng_message.h>
53d7373c82SJulian Elischer #include <netgraph/ng_parse.h>
54d7373c82SJulian Elischer #include <netgraph/netgraph.h>
55d7373c82SJulian Elischer
56d7373c82SJulian Elischer #include <netgraph/ng_checksum.h>
57d7373c82SJulian Elischer
58d7373c82SJulian Elischer /* private data */
59d7373c82SJulian Elischer struct ng_checksum_priv {
60d7373c82SJulian Elischer hook_p in;
61d7373c82SJulian Elischer hook_p out;
62d7373c82SJulian Elischer uint8_t dlt; /* DLT_XXX from bpf.h */
63d7373c82SJulian Elischer struct ng_checksum_config *conf;
64d7373c82SJulian Elischer struct ng_checksum_stats stats;
65d7373c82SJulian Elischer };
66d7373c82SJulian Elischer
67d7373c82SJulian Elischer typedef struct ng_checksum_priv *priv_p;
68d7373c82SJulian Elischer
69d7373c82SJulian Elischer /* Netgraph methods */
70d7373c82SJulian Elischer static ng_constructor_t ng_checksum_constructor;
71d7373c82SJulian Elischer static ng_rcvmsg_t ng_checksum_rcvmsg;
72d7373c82SJulian Elischer static ng_shutdown_t ng_checksum_shutdown;
73d7373c82SJulian Elischer static ng_newhook_t ng_checksum_newhook;
74d7373c82SJulian Elischer static ng_rcvdata_t ng_checksum_rcvdata;
75d7373c82SJulian Elischer static ng_disconnect_t ng_checksum_disconnect;
76d7373c82SJulian Elischer #define ERROUT(x) { error = (x); goto done; }
77d7373c82SJulian Elischer
78d7373c82SJulian Elischer static const struct ng_parse_struct_field ng_checksum_config_type_fields[]
79d7373c82SJulian Elischer = NG_CHECKSUM_CONFIG_TYPE;
80d7373c82SJulian Elischer static const struct ng_parse_type ng_checksum_config_type = {
81d7373c82SJulian Elischer &ng_parse_struct_type,
82d7373c82SJulian Elischer &ng_checksum_config_type_fields
83d7373c82SJulian Elischer };
84d7373c82SJulian Elischer
85d7373c82SJulian Elischer static const struct ng_parse_struct_field ng_checksum_stats_fields[]
86d7373c82SJulian Elischer = NG_CHECKSUM_STATS_TYPE;
87d7373c82SJulian Elischer static const struct ng_parse_type ng_checksum_stats_type = {
88d7373c82SJulian Elischer &ng_parse_struct_type,
89d7373c82SJulian Elischer &ng_checksum_stats_fields
90d7373c82SJulian Elischer };
91d7373c82SJulian Elischer
92d7373c82SJulian Elischer static const struct ng_cmdlist ng_checksum_cmdlist[] = {
93d7373c82SJulian Elischer {
94d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
95d7373c82SJulian Elischer NGM_CHECKSUM_GETDLT,
96d7373c82SJulian Elischer "getdlt",
97d7373c82SJulian Elischer NULL,
98d7373c82SJulian Elischer &ng_parse_uint8_type
99d7373c82SJulian Elischer },
100d7373c82SJulian Elischer {
101d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
102d7373c82SJulian Elischer NGM_CHECKSUM_SETDLT,
103d7373c82SJulian Elischer "setdlt",
104d7373c82SJulian Elischer &ng_parse_uint8_type,
105d7373c82SJulian Elischer NULL
106d7373c82SJulian Elischer },
107d7373c82SJulian Elischer {
108d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
109d7373c82SJulian Elischer NGM_CHECKSUM_GETCONFIG,
110d7373c82SJulian Elischer "getconfig",
111d7373c82SJulian Elischer NULL,
112d7373c82SJulian Elischer &ng_checksum_config_type
113d7373c82SJulian Elischer },
114d7373c82SJulian Elischer {
115d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
116d7373c82SJulian Elischer NGM_CHECKSUM_SETCONFIG,
117d7373c82SJulian Elischer "setconfig",
118d7373c82SJulian Elischer &ng_checksum_config_type,
119d7373c82SJulian Elischer NULL
120d7373c82SJulian Elischer },
121d7373c82SJulian Elischer {
122d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
123d7373c82SJulian Elischer NGM_CHECKSUM_GET_STATS,
124d7373c82SJulian Elischer "getstats",
125d7373c82SJulian Elischer NULL,
126d7373c82SJulian Elischer &ng_checksum_stats_type
127d7373c82SJulian Elischer },
128d7373c82SJulian Elischer {
129d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
130d7373c82SJulian Elischer NGM_CHECKSUM_CLR_STATS,
131d7373c82SJulian Elischer "clrstats",
132d7373c82SJulian Elischer NULL,
133d7373c82SJulian Elischer NULL
134d7373c82SJulian Elischer },
135d7373c82SJulian Elischer {
136d7373c82SJulian Elischer NGM_CHECKSUM_COOKIE,
137d7373c82SJulian Elischer NGM_CHECKSUM_GETCLR_STATS,
138d7373c82SJulian Elischer "getclrstats",
139d7373c82SJulian Elischer NULL,
140d7373c82SJulian Elischer &ng_checksum_stats_type
141d7373c82SJulian Elischer },
142d7373c82SJulian Elischer { 0 }
143d7373c82SJulian Elischer };
144d7373c82SJulian Elischer
145d7373c82SJulian Elischer static struct ng_type typestruct = {
146d7373c82SJulian Elischer .version = NG_ABI_VERSION,
147d7373c82SJulian Elischer .name = NG_CHECKSUM_NODE_TYPE,
148d7373c82SJulian Elischer .constructor = ng_checksum_constructor,
149d7373c82SJulian Elischer .rcvmsg = ng_checksum_rcvmsg,
150d7373c82SJulian Elischer .shutdown = ng_checksum_shutdown,
151d7373c82SJulian Elischer .newhook = ng_checksum_newhook,
152d7373c82SJulian Elischer .rcvdata = ng_checksum_rcvdata,
153d7373c82SJulian Elischer .disconnect = ng_checksum_disconnect,
154d7373c82SJulian Elischer .cmdlist = ng_checksum_cmdlist,
155d7373c82SJulian Elischer };
156d7373c82SJulian Elischer
157d7373c82SJulian Elischer NETGRAPH_INIT(checksum, &typestruct);
158d7373c82SJulian Elischer
159d7373c82SJulian Elischer static int
ng_checksum_constructor(node_p node)160d7373c82SJulian Elischer ng_checksum_constructor(node_p node)
161d7373c82SJulian Elischer {
162d7373c82SJulian Elischer priv_p priv;
163d7373c82SJulian Elischer
164d7373c82SJulian Elischer priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK|M_ZERO);
165d7373c82SJulian Elischer priv->dlt = DLT_RAW;
166d7373c82SJulian Elischer
167d7373c82SJulian Elischer NG_NODE_SET_PRIVATE(node, priv);
168d7373c82SJulian Elischer
169d7373c82SJulian Elischer return (0);
170d7373c82SJulian Elischer }
171d7373c82SJulian Elischer
172d7373c82SJulian Elischer static int
ng_checksum_newhook(node_p node,hook_p hook,const char * name)173d7373c82SJulian Elischer ng_checksum_newhook(node_p node, hook_p hook, const char *name)
174d7373c82SJulian Elischer {
175d7373c82SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
176d7373c82SJulian Elischer
177d7373c82SJulian Elischer if (strncmp(name, NG_CHECKSUM_HOOK_IN, strlen(NG_CHECKSUM_HOOK_IN)) == 0) {
178d7373c82SJulian Elischer priv->in = hook;
179d7373c82SJulian Elischer } else if (strncmp(name, NG_CHECKSUM_HOOK_OUT, strlen(NG_CHECKSUM_HOOK_OUT)) == 0) {
180d7373c82SJulian Elischer priv->out = hook;
181d7373c82SJulian Elischer } else
182d7373c82SJulian Elischer return (EINVAL);
183d7373c82SJulian Elischer
184d7373c82SJulian Elischer return (0);
185d7373c82SJulian Elischer }
186d7373c82SJulian Elischer
187d7373c82SJulian Elischer static int
ng_checksum_rcvmsg(node_p node,item_p item,hook_p lasthook)188d7373c82SJulian Elischer ng_checksum_rcvmsg(node_p node, item_p item, hook_p lasthook)
189d7373c82SJulian Elischer {
190d7373c82SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
191d7373c82SJulian Elischer struct ng_checksum_config *conf, *newconf;
192d7373c82SJulian Elischer struct ng_mesg *msg;
193d7373c82SJulian Elischer struct ng_mesg *resp = NULL;
194d7373c82SJulian Elischer int error = 0;
195d7373c82SJulian Elischer
196d7373c82SJulian Elischer NGI_GET_MSG(item, msg);
197d7373c82SJulian Elischer
198d7373c82SJulian Elischer if (msg->header.typecookie != NGM_CHECKSUM_COOKIE)
199d7373c82SJulian Elischer ERROUT(EINVAL);
200d7373c82SJulian Elischer
201d7373c82SJulian Elischer switch (msg->header.cmd)
202d7373c82SJulian Elischer {
203d7373c82SJulian Elischer case NGM_CHECKSUM_GETDLT:
204d7373c82SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
205d7373c82SJulian Elischer
206d7373c82SJulian Elischer if (resp == NULL)
207d7373c82SJulian Elischer ERROUT(ENOMEM);
208d7373c82SJulian Elischer
209d7373c82SJulian Elischer *((uint8_t *) resp->data) = priv->dlt;
210d7373c82SJulian Elischer
211d7373c82SJulian Elischer break;
212d7373c82SJulian Elischer
213d7373c82SJulian Elischer case NGM_CHECKSUM_SETDLT:
214d7373c82SJulian Elischer if (msg->header.arglen != sizeof(uint8_t))
215d7373c82SJulian Elischer ERROUT(EINVAL);
216d7373c82SJulian Elischer
217d7373c82SJulian Elischer switch (*(uint8_t *) msg->data)
218d7373c82SJulian Elischer {
219d7373c82SJulian Elischer case DLT_EN10MB:
220d7373c82SJulian Elischer case DLT_RAW:
221d7373c82SJulian Elischer priv->dlt = *(uint8_t *) msg->data;
222d7373c82SJulian Elischer break;
223d7373c82SJulian Elischer
224d7373c82SJulian Elischer default:
225d7373c82SJulian Elischer ERROUT(EINVAL);
226d7373c82SJulian Elischer }
227d7373c82SJulian Elischer
228d7373c82SJulian Elischer break;
229d7373c82SJulian Elischer
230d7373c82SJulian Elischer case NGM_CHECKSUM_GETCONFIG:
231d7373c82SJulian Elischer if (priv->conf == NULL)
232d7373c82SJulian Elischer ERROUT(0);
233d7373c82SJulian Elischer
234d7373c82SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_config), M_WAITOK);
235d7373c82SJulian Elischer
236d7373c82SJulian Elischer if (resp == NULL)
237d7373c82SJulian Elischer ERROUT(ENOMEM);
238d7373c82SJulian Elischer
239d7373c82SJulian Elischer bcopy(priv->conf, resp->data, sizeof(struct ng_checksum_config));
240d7373c82SJulian Elischer
241d7373c82SJulian Elischer break;
242d7373c82SJulian Elischer
243d7373c82SJulian Elischer case NGM_CHECKSUM_SETCONFIG:
244d7373c82SJulian Elischer conf = (struct ng_checksum_config *) msg->data;
245d7373c82SJulian Elischer
246d7373c82SJulian Elischer if (msg->header.arglen != sizeof(struct ng_checksum_config))
247d7373c82SJulian Elischer ERROUT(EINVAL);
248d7373c82SJulian Elischer
249d7373c82SJulian Elischer conf->csum_flags &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
250d7373c82SJulian Elischer conf->csum_offload &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
251d7373c82SJulian Elischer
252d7373c82SJulian Elischer newconf = malloc(sizeof(struct ng_checksum_config), M_NETGRAPH, M_WAITOK|M_ZERO);
253d7373c82SJulian Elischer
254d7373c82SJulian Elischer bcopy(conf, newconf, sizeof(struct ng_checksum_config));
255d7373c82SJulian Elischer
256d7373c82SJulian Elischer if (priv->conf)
257d7373c82SJulian Elischer free(priv->conf, M_NETGRAPH);
258d7373c82SJulian Elischer
259d7373c82SJulian Elischer priv->conf = newconf;
260d7373c82SJulian Elischer
261d7373c82SJulian Elischer break;
262d7373c82SJulian Elischer
263d7373c82SJulian Elischer case NGM_CHECKSUM_GET_STATS:
264d7373c82SJulian Elischer case NGM_CHECKSUM_CLR_STATS:
265d7373c82SJulian Elischer case NGM_CHECKSUM_GETCLR_STATS:
266d7373c82SJulian Elischer if (msg->header.cmd != NGM_CHECKSUM_CLR_STATS) {
267d7373c82SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_stats), M_WAITOK);
268d7373c82SJulian Elischer
269d7373c82SJulian Elischer if (resp == NULL)
270d7373c82SJulian Elischer ERROUT(ENOMEM);
271d7373c82SJulian Elischer
272d7373c82SJulian Elischer bcopy(&(priv->stats), resp->data, sizeof(struct ng_checksum_stats));
273d7373c82SJulian Elischer }
274d7373c82SJulian Elischer
275d7373c82SJulian Elischer if (msg->header.cmd != NGM_CHECKSUM_GET_STATS)
276d7373c82SJulian Elischer bzero(&(priv->stats), sizeof(struct ng_checksum_stats));
277d7373c82SJulian Elischer
278d7373c82SJulian Elischer break;
279d7373c82SJulian Elischer
280d7373c82SJulian Elischer default:
281d7373c82SJulian Elischer ERROUT(EINVAL);
282d7373c82SJulian Elischer }
283d7373c82SJulian Elischer
284d7373c82SJulian Elischer done:
285d7373c82SJulian Elischer NG_RESPOND_MSG(error, node, item, resp);
286d7373c82SJulian Elischer NG_FREE_MSG(msg);
287d7373c82SJulian Elischer
288d7373c82SJulian Elischer return (error);
289d7373c82SJulian Elischer }
290d7373c82SJulian Elischer
291d7373c82SJulian Elischer #define PULLUP_CHECK(mbuf, length) do { \
292d7373c82SJulian Elischer pullup_len += length; \
293d7373c82SJulian Elischer if (((mbuf)->m_pkthdr.len < pullup_len) || \
294d7373c82SJulian Elischer (pullup_len > MHLEN)) { \
295d7373c82SJulian Elischer return (EINVAL); \
296d7373c82SJulian Elischer } \
297d7373c82SJulian Elischer if ((mbuf)->m_len < pullup_len && \
298d7373c82SJulian Elischer (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \
299d7373c82SJulian Elischer return (ENOBUFS); \
300d7373c82SJulian Elischer } \
301d7373c82SJulian Elischer } while (0)
302d7373c82SJulian Elischer
303d7373c82SJulian Elischer #ifdef INET
304d7373c82SJulian Elischer static int
checksum_ipv4(priv_p priv,struct mbuf * m,int l3_offset)305d7373c82SJulian Elischer checksum_ipv4(priv_p priv, struct mbuf *m, int l3_offset)
306d7373c82SJulian Elischer {
307d7373c82SJulian Elischer struct ip *ip4;
308d7373c82SJulian Elischer int pullup_len;
309d7373c82SJulian Elischer int hlen, plen;
310d7373c82SJulian Elischer int processed = 0;
311d7373c82SJulian Elischer
312d7373c82SJulian Elischer pullup_len = l3_offset;
313d7373c82SJulian Elischer
314d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ip));
315d7373c82SJulian Elischer ip4 = (struct ip *) mtodo(m, l3_offset);
316d7373c82SJulian Elischer
317d7373c82SJulian Elischer if (ip4->ip_v != IPVERSION)
318d7373c82SJulian Elischer return (EOPNOTSUPP);
319d7373c82SJulian Elischer
320d7373c82SJulian Elischer hlen = ip4->ip_hl << 2;
321d7373c82SJulian Elischer plen = ntohs(ip4->ip_len);
322d7373c82SJulian Elischer
323d7373c82SJulian Elischer if (hlen < sizeof(struct ip) || m->m_pkthdr.len < l3_offset + plen)
324d7373c82SJulian Elischer return (EINVAL);
325d7373c82SJulian Elischer
326d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & CSUM_IP) {
327d7373c82SJulian Elischer ip4->ip_sum = 0;
328d7373c82SJulian Elischer
329d7373c82SJulian Elischer if ((priv->conf->csum_offload & CSUM_IP) == 0) {
330d7373c82SJulian Elischer if (hlen == sizeof(struct ip))
331d7373c82SJulian Elischer ip4->ip_sum = in_cksum_hdr(ip4);
332d7373c82SJulian Elischer else
333d7373c82SJulian Elischer ip4->ip_sum = in_cksum_skip(m, l3_offset + hlen, l3_offset);
334d7373c82SJulian Elischer
335d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_IP;
336d7373c82SJulian Elischer }
337d7373c82SJulian Elischer
338d7373c82SJulian Elischer processed = 1;
339d7373c82SJulian Elischer }
340d7373c82SJulian Elischer
341d7373c82SJulian Elischer pullup_len = l3_offset + hlen;
342d7373c82SJulian Elischer
343d7373c82SJulian Elischer /* We can not calculate a checksum fragmented packets */
344d7373c82SJulian Elischer if (ip4->ip_off & htons(IP_MF|IP_OFFMASK)) {
345d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
346d7373c82SJulian Elischer return (0);
347d7373c82SJulian Elischer }
348d7373c82SJulian Elischer
349d7373c82SJulian Elischer switch (ip4->ip_p)
350d7373c82SJulian Elischer {
351d7373c82SJulian Elischer case IPPROTO_TCP:
352d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & CSUM_TCP) {
353d7373c82SJulian Elischer struct tcphdr *th;
354d7373c82SJulian Elischer
355d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct tcphdr));
356d7373c82SJulian Elischer th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
357d7373c82SJulian Elischer
358d7373c82SJulian Elischer th->th_sum = in_pseudo(ip4->ip_src.s_addr,
359d7373c82SJulian Elischer ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
360d7373c82SJulian Elischer
361d7373c82SJulian Elischer if ((priv->conf->csum_offload & CSUM_TCP) == 0) {
362d7373c82SJulian Elischer th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
363d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_TCP;
364d7373c82SJulian Elischer }
365d7373c82SJulian Elischer
366d7373c82SJulian Elischer processed = 1;
367d7373c82SJulian Elischer }
368d7373c82SJulian Elischer
369d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_UDP;
370d7373c82SJulian Elischer break;
371d7373c82SJulian Elischer
372d7373c82SJulian Elischer case IPPROTO_UDP:
373d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & CSUM_UDP) {
374d7373c82SJulian Elischer struct udphdr *uh;
375d7373c82SJulian Elischer
376d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct udphdr));
377d7373c82SJulian Elischer uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
378d7373c82SJulian Elischer
379d7373c82SJulian Elischer uh->uh_sum = in_pseudo(ip4->ip_src.s_addr,
380d7373c82SJulian Elischer ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
381d7373c82SJulian Elischer
382d7373c82SJulian Elischer if ((priv->conf->csum_offload & CSUM_UDP) == 0) {
383d7373c82SJulian Elischer uh->uh_sum = in_cksum_skip(m,
384d7373c82SJulian Elischer l3_offset + plen, l3_offset + hlen);
385d7373c82SJulian Elischer
386d7373c82SJulian Elischer if (uh->uh_sum == 0)
387d7373c82SJulian Elischer uh->uh_sum = 0xffff;
388d7373c82SJulian Elischer
389d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_UDP;
390d7373c82SJulian Elischer }
391d7373c82SJulian Elischer
392d7373c82SJulian Elischer processed = 1;
393d7373c82SJulian Elischer }
394d7373c82SJulian Elischer
395d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_TCP;
396d7373c82SJulian Elischer break;
397d7373c82SJulian Elischer
398d7373c82SJulian Elischer default:
399d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
400d7373c82SJulian Elischer break;
401d7373c82SJulian Elischer }
402d7373c82SJulian Elischer
403d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV6;
404d7373c82SJulian Elischer
405d7373c82SJulian Elischer if (processed)
406d7373c82SJulian Elischer priv->stats.processed++;
407d7373c82SJulian Elischer
408d7373c82SJulian Elischer return (0);
409d7373c82SJulian Elischer }
410d7373c82SJulian Elischer #endif /* INET */
411d7373c82SJulian Elischer
412d7373c82SJulian Elischer #ifdef INET6
413d7373c82SJulian Elischer static int
checksum_ipv6(priv_p priv,struct mbuf * m,int l3_offset)414d7373c82SJulian Elischer checksum_ipv6(priv_p priv, struct mbuf *m, int l3_offset)
415d7373c82SJulian Elischer {
416d7373c82SJulian Elischer struct ip6_hdr *ip6;
417d7373c82SJulian Elischer struct ip6_ext *ip6e = NULL;
418d7373c82SJulian Elischer int pullup_len;
419d7373c82SJulian Elischer int hlen, plen;
420d7373c82SJulian Elischer int nxt;
421d7373c82SJulian Elischer int processed = 0;
422d7373c82SJulian Elischer
423d7373c82SJulian Elischer pullup_len = l3_offset;
424d7373c82SJulian Elischer
425d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ip6_hdr));
426d7373c82SJulian Elischer ip6 = (struct ip6_hdr *) mtodo(m, l3_offset);
427d7373c82SJulian Elischer
428d7373c82SJulian Elischer if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
429d7373c82SJulian Elischer return (EOPNOTSUPP);
430d7373c82SJulian Elischer
431d7373c82SJulian Elischer hlen = sizeof(struct ip6_hdr);
432d7373c82SJulian Elischer plen = ntohs(ip6->ip6_plen) + hlen;
433d7373c82SJulian Elischer
434d7373c82SJulian Elischer if (m->m_pkthdr.len < l3_offset + plen)
435d7373c82SJulian Elischer return (EINVAL);
436d7373c82SJulian Elischer
437d7373c82SJulian Elischer nxt = ip6->ip6_nxt;
438d7373c82SJulian Elischer
439d7373c82SJulian Elischer for (;;) {
440d7373c82SJulian Elischer switch (nxt)
441d7373c82SJulian Elischer {
442d7373c82SJulian Elischer case IPPROTO_DSTOPTS:
443d7373c82SJulian Elischer case IPPROTO_HOPOPTS:
444d7373c82SJulian Elischer case IPPROTO_ROUTING:
445d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ip6_ext));
446d7373c82SJulian Elischer ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
447d7373c82SJulian Elischer nxt = ip6e->ip6e_nxt;
448d7373c82SJulian Elischer hlen += (ip6e->ip6e_len + 1) << 3;
449d7373c82SJulian Elischer pullup_len = l3_offset + hlen;
450d7373c82SJulian Elischer break;
451d7373c82SJulian Elischer
452d7373c82SJulian Elischer case IPPROTO_AH:
453d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ip6_ext));
454d7373c82SJulian Elischer ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
455d7373c82SJulian Elischer nxt = ip6e->ip6e_nxt;
456d7373c82SJulian Elischer hlen += (ip6e->ip6e_len + 2) << 2;
457d7373c82SJulian Elischer pullup_len = l3_offset + hlen;
458d7373c82SJulian Elischer break;
459d7373c82SJulian Elischer
460d7373c82SJulian Elischer case IPPROTO_FRAGMENT:
461d7373c82SJulian Elischer /* We can not calculate a checksum fragmented packets */
462d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
463d7373c82SJulian Elischer return (0);
464d7373c82SJulian Elischer
465d7373c82SJulian Elischer default:
466d7373c82SJulian Elischer goto loopend;
467d7373c82SJulian Elischer }
468d7373c82SJulian Elischer
469d7373c82SJulian Elischer if (nxt == 0)
470d7373c82SJulian Elischer return (EINVAL);
471d7373c82SJulian Elischer }
472d7373c82SJulian Elischer
473d7373c82SJulian Elischer loopend:
474d7373c82SJulian Elischer
475d7373c82SJulian Elischer switch (nxt)
476d7373c82SJulian Elischer {
477d7373c82SJulian Elischer case IPPROTO_TCP:
478d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & CSUM_TCP_IPV6) {
479d7373c82SJulian Elischer struct tcphdr *th;
480d7373c82SJulian Elischer
481d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct tcphdr));
482d7373c82SJulian Elischer th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
483d7373c82SJulian Elischer
484d7373c82SJulian Elischer th->th_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
485d7373c82SJulian Elischer
486d7373c82SJulian Elischer if ((priv->conf->csum_offload & CSUM_TCP_IPV6) == 0) {
487d7373c82SJulian Elischer th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
488d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
489d7373c82SJulian Elischer }
490d7373c82SJulian Elischer
491d7373c82SJulian Elischer processed = 1;
492d7373c82SJulian Elischer }
493d7373c82SJulian Elischer
494d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
495d7373c82SJulian Elischer break;
496d7373c82SJulian Elischer
497d7373c82SJulian Elischer case IPPROTO_UDP:
498d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6) {
499d7373c82SJulian Elischer struct udphdr *uh;
500d7373c82SJulian Elischer
501d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct udphdr));
502d7373c82SJulian Elischer uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
503d7373c82SJulian Elischer
504d7373c82SJulian Elischer uh->uh_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
505d7373c82SJulian Elischer
506d7373c82SJulian Elischer if ((priv->conf->csum_offload & CSUM_UDP_IPV6) == 0) {
507d7373c82SJulian Elischer uh->uh_sum = in_cksum_skip(m,
508d7373c82SJulian Elischer l3_offset + plen, l3_offset + hlen);
509d7373c82SJulian Elischer
510d7373c82SJulian Elischer if (uh->uh_sum == 0)
511d7373c82SJulian Elischer uh->uh_sum = 0xffff;
512d7373c82SJulian Elischer
513d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
514d7373c82SJulian Elischer }
515d7373c82SJulian Elischer
516d7373c82SJulian Elischer processed = 1;
517d7373c82SJulian Elischer }
518d7373c82SJulian Elischer
519d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
520d7373c82SJulian Elischer break;
521d7373c82SJulian Elischer
522d7373c82SJulian Elischer default:
523d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
524d7373c82SJulian Elischer break;
525d7373c82SJulian Elischer }
526d7373c82SJulian Elischer
527d7373c82SJulian Elischer m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV4;
528d7373c82SJulian Elischer
529d7373c82SJulian Elischer if (processed)
530d7373c82SJulian Elischer priv->stats.processed++;
531d7373c82SJulian Elischer
532d7373c82SJulian Elischer return (0);
533d7373c82SJulian Elischer }
534d7373c82SJulian Elischer #endif /* INET6 */
535d7373c82SJulian Elischer
536d7373c82SJulian Elischer #undef PULLUP_CHECK
537d7373c82SJulian Elischer
538d7373c82SJulian Elischer static int
ng_checksum_rcvdata(hook_p hook,item_p item)539d7373c82SJulian Elischer ng_checksum_rcvdata(hook_p hook, item_p item)
540d7373c82SJulian Elischer {
541d7373c82SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
542d7373c82SJulian Elischer struct mbuf *m;
543d7373c82SJulian Elischer hook_p out;
544d7373c82SJulian Elischer int error = 0;
545d7373c82SJulian Elischer
546d7373c82SJulian Elischer priv->stats.received++;
547d7373c82SJulian Elischer
548d7373c82SJulian Elischer NGI_GET_M(item, m);
549d7373c82SJulian Elischer
550d7373c82SJulian Elischer #define PULLUP_CHECK(mbuf, length) do { \
551d7373c82SJulian Elischer pullup_len += length; \
552d7373c82SJulian Elischer if (((mbuf)->m_pkthdr.len < pullup_len) || \
553d7373c82SJulian Elischer (pullup_len > MHLEN)) { \
554d7373c82SJulian Elischer error = EINVAL; \
555d7373c82SJulian Elischer goto bypass; \
556d7373c82SJulian Elischer } \
557d7373c82SJulian Elischer if ((mbuf)->m_len < pullup_len && \
558d7373c82SJulian Elischer (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \
559d7373c82SJulian Elischer error = ENOBUFS; \
560d7373c82SJulian Elischer goto drop; \
561d7373c82SJulian Elischer } \
562d7373c82SJulian Elischer } while (0)
563d7373c82SJulian Elischer
564d7373c82SJulian Elischer if (!(priv->conf && hook == priv->in && m && (m->m_flags & M_PKTHDR)))
565d7373c82SJulian Elischer goto bypass;
566d7373c82SJulian Elischer
567d7373c82SJulian Elischer m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
568d7373c82SJulian Elischer
569d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & (NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6))
570d7373c82SJulian Elischer {
571d7373c82SJulian Elischer struct ether_header *eh;
572d7373c82SJulian Elischer struct ng_checksum_vlan_header *vh;
573d7373c82SJulian Elischer int pullup_len = 0;
574d7373c82SJulian Elischer uint16_t etype;
575d7373c82SJulian Elischer
576d7373c82SJulian Elischer m = m_unshare(m, M_NOWAIT);
577d7373c82SJulian Elischer
578d7373c82SJulian Elischer if (m == NULL)
579d7373c82SJulian Elischer ERROUT(ENOMEM);
580d7373c82SJulian Elischer
581d7373c82SJulian Elischer switch (priv->dlt)
582d7373c82SJulian Elischer {
583d7373c82SJulian Elischer case DLT_EN10MB:
584d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ether_header));
585d7373c82SJulian Elischer eh = mtod(m, struct ether_header *);
586d7373c82SJulian Elischer etype = ntohs(eh->ether_type);
587d7373c82SJulian Elischer
588d7373c82SJulian Elischer for (;;) { /* QinQ support */
589d7373c82SJulian Elischer switch (etype)
590d7373c82SJulian Elischer {
591d7373c82SJulian Elischer case 0x8100:
592d7373c82SJulian Elischer case 0x88A8:
593d7373c82SJulian Elischer case 0x9100:
594d7373c82SJulian Elischer PULLUP_CHECK(m, sizeof(struct ng_checksum_vlan_header));
595d7373c82SJulian Elischer vh = (struct ng_checksum_vlan_header *) mtodo(m,
596d7373c82SJulian Elischer pullup_len - sizeof(struct ng_checksum_vlan_header));
597d7373c82SJulian Elischer etype = ntohs(vh->etype);
598d7373c82SJulian Elischer break;
599d7373c82SJulian Elischer
600d7373c82SJulian Elischer default:
601d7373c82SJulian Elischer goto loopend;
602d7373c82SJulian Elischer }
603d7373c82SJulian Elischer }
604d7373c82SJulian Elischer loopend:
605d7373c82SJulian Elischer #ifdef INET
606d7373c82SJulian Elischer if (etype == ETHERTYPE_IP &&
607d7373c82SJulian Elischer (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)) {
608d7373c82SJulian Elischer error = checksum_ipv4(priv, m, pullup_len);
609d7373c82SJulian Elischer if (error == ENOBUFS)
610d7373c82SJulian Elischer goto drop;
611d7373c82SJulian Elischer } else
612d7373c82SJulian Elischer #endif
613d7373c82SJulian Elischer #ifdef INET6
614d7373c82SJulian Elischer if (etype == ETHERTYPE_IPV6 &&
615d7373c82SJulian Elischer (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)) {
616d7373c82SJulian Elischer error = checksum_ipv6(priv, m, pullup_len);
617d7373c82SJulian Elischer if (error == ENOBUFS)
618d7373c82SJulian Elischer goto drop;
619d7373c82SJulian Elischer } else
620d7373c82SJulian Elischer #endif
621d7373c82SJulian Elischer {
622d7373c82SJulian Elischer m->m_pkthdr.csum_flags &=
623d7373c82SJulian Elischer ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
624d7373c82SJulian Elischer }
625d7373c82SJulian Elischer
626d7373c82SJulian Elischer break;
627d7373c82SJulian Elischer
628d7373c82SJulian Elischer case DLT_RAW:
629d7373c82SJulian Elischer #ifdef INET
630d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)
631d7373c82SJulian Elischer {
632d7373c82SJulian Elischer error = checksum_ipv4(priv, m, pullup_len);
633d7373c82SJulian Elischer
634d7373c82SJulian Elischer if (error == 0)
635d7373c82SJulian Elischer goto bypass;
636d7373c82SJulian Elischer else if (error == ENOBUFS)
637d7373c82SJulian Elischer goto drop;
638d7373c82SJulian Elischer }
639d7373c82SJulian Elischer #endif
640d7373c82SJulian Elischer #ifdef INET6
641d7373c82SJulian Elischer if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)
642d7373c82SJulian Elischer {
643d7373c82SJulian Elischer error = checksum_ipv6(priv, m, pullup_len);
644d7373c82SJulian Elischer
645d7373c82SJulian Elischer if (error == 0)
646d7373c82SJulian Elischer goto bypass;
647d7373c82SJulian Elischer else if (error == ENOBUFS)
648d7373c82SJulian Elischer goto drop;
649d7373c82SJulian Elischer }
650d7373c82SJulian Elischer #endif
651d7373c82SJulian Elischer if (error)
652d7373c82SJulian Elischer m->m_pkthdr.csum_flags &=
653d7373c82SJulian Elischer ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
654d7373c82SJulian Elischer
655d7373c82SJulian Elischer break;
656d7373c82SJulian Elischer
657d7373c82SJulian Elischer default:
658d7373c82SJulian Elischer ERROUT(EINVAL);
659d7373c82SJulian Elischer }
660d7373c82SJulian Elischer }
661d7373c82SJulian Elischer
662d7373c82SJulian Elischer #undef PULLUP_CHECK
663d7373c82SJulian Elischer
664d7373c82SJulian Elischer bypass:
665d7373c82SJulian Elischer out = NULL;
666d7373c82SJulian Elischer
667d7373c82SJulian Elischer if (hook == priv->in) {
668d7373c82SJulian Elischer /* return frames on 'in' hook if 'out' not connected */
669d7373c82SJulian Elischer out = priv->out ? priv->out : priv->in;
670d7373c82SJulian Elischer } else if (hook == priv->out && priv->in) {
671d7373c82SJulian Elischer /* pass frames on 'out' hook if 'in' connected */
672d7373c82SJulian Elischer out = priv->in;
673d7373c82SJulian Elischer }
674d7373c82SJulian Elischer
675d7373c82SJulian Elischer if (out == NULL)
676d7373c82SJulian Elischer ERROUT(0);
677d7373c82SJulian Elischer
678d7373c82SJulian Elischer NG_FWD_NEW_DATA(error, item, out, m);
679d7373c82SJulian Elischer
680d7373c82SJulian Elischer return (error);
681d7373c82SJulian Elischer
682d7373c82SJulian Elischer done:
683*687e510eSLutz Donnerhacke NG_FREE_M(m);
684d7373c82SJulian Elischer drop:
685d7373c82SJulian Elischer NG_FREE_ITEM(item);
686d7373c82SJulian Elischer
687d7373c82SJulian Elischer priv->stats.dropped++;
688d7373c82SJulian Elischer
689d7373c82SJulian Elischer return (error);
690d7373c82SJulian Elischer }
691d7373c82SJulian Elischer
692d7373c82SJulian Elischer static int
ng_checksum_shutdown(node_p node)693d7373c82SJulian Elischer ng_checksum_shutdown(node_p node)
694d7373c82SJulian Elischer {
695d7373c82SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
696d7373c82SJulian Elischer
697d7373c82SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL);
698d7373c82SJulian Elischer NG_NODE_UNREF(node);
699d7373c82SJulian Elischer
700d7373c82SJulian Elischer if (priv->conf)
701d7373c82SJulian Elischer free(priv->conf, M_NETGRAPH);
702d7373c82SJulian Elischer
703d7373c82SJulian Elischer free(priv, M_NETGRAPH);
704d7373c82SJulian Elischer
705d7373c82SJulian Elischer return (0);
706d7373c82SJulian Elischer }
707d7373c82SJulian Elischer
708d7373c82SJulian Elischer static int
ng_checksum_disconnect(hook_p hook)709d7373c82SJulian Elischer ng_checksum_disconnect(hook_p hook)
710d7373c82SJulian Elischer {
711d7373c82SJulian Elischer priv_p priv;
712d7373c82SJulian Elischer
713d7373c82SJulian Elischer priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
714d7373c82SJulian Elischer
715d7373c82SJulian Elischer if (hook == priv->in)
716d7373c82SJulian Elischer priv->in = NULL;
717d7373c82SJulian Elischer
718d7373c82SJulian Elischer if (hook == priv->out)
719d7373c82SJulian Elischer priv->out = NULL;
720d7373c82SJulian Elischer
721d7373c82SJulian Elischer if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
722d7373c82SJulian Elischer NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
723d7373c82SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook));
724d7373c82SJulian Elischer
725d7373c82SJulian Elischer return (0);
726d7373c82SJulian Elischer }
727