1303612Sjulian/*-
2303612Sjulian * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru>
3303612Sjulian * All rights reserved.
4303612Sjulian *
5303612Sjulian * Redistribution and use in source and binary forms, with or without
6303612Sjulian * modification, are permitted provided that the following conditions
7303612Sjulian * are met:
8303612Sjulian * 1. Redistributions of source code must retain the above copyright
9303612Sjulian *    notice, this list of conditions and the following disclaimer.
10303612Sjulian * 2. Redistributions in binary form must reproduce the above copyright
11303612Sjulian *    notice, this list of conditions and the following disclaimer in the
12303612Sjulian *    documentation and/or other materials provided with the distribution.
13303612Sjulian *
14303612Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15303612Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16303612Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17303612Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18303612Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19303612Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20303612Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21303612Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22303612Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23303612Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24303612Sjulian * SUCH DAMAGE.
25303612Sjulian *
26303612Sjulian */
27303612Sjulian
28303612Sjulian#include <sys/cdefs.h>
29303612Sjulian__FBSDID("$FreeBSD: stable/10/sys/netgraph/ng_checksum.c 309387 2016-12-02 05:38:25Z julian $");
30303612Sjulian
31303612Sjulian#include "opt_inet.h"
32303612Sjulian#include "opt_inet6.h"
33303612Sjulian
34303612Sjulian#include <sys/param.h>
35303612Sjulian#include <sys/systm.h>
36303612Sjulian#include <sys/kernel.h>
37303612Sjulian#include <sys/endian.h>
38303612Sjulian#include <sys/malloc.h>
39303612Sjulian#include <sys/mbuf.h>
40303612Sjulian#include <sys/socket.h>
41303612Sjulian
42303612Sjulian#include <net/bpf.h>
43303612Sjulian#include <net/ethernet.h>
44303612Sjulian#include <net/if.h>
45303612Sjulian#include <net/if_vlan_var.h>
46303612Sjulian
47303612Sjulian#include <netinet/in.h>
48303612Sjulian#include <netinet/ip.h>
49303612Sjulian#include <netinet/ip6.h>
50303612Sjulian#include <netinet/tcp.h>
51303612Sjulian#include <netinet/udp.h>
52303612Sjulian#include <machine/in_cksum.h>
53303612Sjulian
54303612Sjulian#include <netgraph/ng_message.h>
55303612Sjulian#include <netgraph/ng_parse.h>
56303612Sjulian#include <netgraph/netgraph.h>
57303612Sjulian
58303612Sjulian#include <netgraph/ng_checksum.h>
59303612Sjulian
60303612Sjulian/* private data */
61303612Sjulianstruct ng_checksum_priv {
62303612Sjulian	hook_p in;
63303612Sjulian	hook_p out;
64303612Sjulian	uint8_t dlt;	/* DLT_XXX from bpf.h */
65303612Sjulian	struct ng_checksum_config *conf;
66303612Sjulian	struct ng_checksum_stats stats;
67303612Sjulian};
68303612Sjulian
69303612Sjuliantypedef struct ng_checksum_priv *priv_p;
70303612Sjulian
71303612Sjulian/* Netgraph methods */
72303612Sjulianstatic ng_constructor_t	ng_checksum_constructor;
73303612Sjulianstatic ng_rcvmsg_t	ng_checksum_rcvmsg;
74303612Sjulianstatic ng_shutdown_t	ng_checksum_shutdown;
75303612Sjulianstatic ng_newhook_t	ng_checksum_newhook;
76303612Sjulianstatic ng_rcvdata_t	ng_checksum_rcvdata;
77303612Sjulianstatic ng_disconnect_t	ng_checksum_disconnect;
78303612Sjulian
79303612Sjulian#define ERROUT(x) { error = (x); goto done; }
80303612Sjulian
81303612Sjulianstatic const struct ng_parse_struct_field ng_checksum_config_type_fields[]
82303612Sjulian	= NG_CHECKSUM_CONFIG_TYPE;
83303612Sjulianstatic const struct ng_parse_type ng_checksum_config_type = {
84303612Sjulian	&ng_parse_struct_type,
85303612Sjulian	&ng_checksum_config_type_fields
86303612Sjulian};
87303612Sjulian
88303612Sjulianstatic const struct ng_parse_struct_field ng_checksum_stats_fields[]
89303612Sjulian	= NG_CHECKSUM_STATS_TYPE;
90303612Sjulianstatic const struct ng_parse_type ng_checksum_stats_type = {
91303612Sjulian	&ng_parse_struct_type,
92303612Sjulian	&ng_checksum_stats_fields
93303612Sjulian};
94303612Sjulian
95303612Sjulianstatic const struct ng_cmdlist ng_checksum_cmdlist[] = {
96303612Sjulian	{
97303612Sjulian		NGM_CHECKSUM_COOKIE,
98303612Sjulian		NGM_CHECKSUM_GETDLT,
99303612Sjulian		"getdlt",
100303612Sjulian		NULL,
101303612Sjulian		&ng_parse_uint8_type
102303612Sjulian	},
103303612Sjulian	{
104303612Sjulian		NGM_CHECKSUM_COOKIE,
105303612Sjulian		NGM_CHECKSUM_SETDLT,
106303612Sjulian		"setdlt",
107303612Sjulian		&ng_parse_uint8_type,
108303612Sjulian		NULL
109303612Sjulian	},
110303612Sjulian	{
111303612Sjulian		NGM_CHECKSUM_COOKIE,
112303612Sjulian		NGM_CHECKSUM_GETCONFIG,
113303612Sjulian		"getconfig",
114303612Sjulian		NULL,
115303612Sjulian		&ng_checksum_config_type
116303612Sjulian	},
117303612Sjulian	{
118303612Sjulian		NGM_CHECKSUM_COOKIE,
119303612Sjulian		NGM_CHECKSUM_SETCONFIG,
120303612Sjulian		"setconfig",
121303612Sjulian		&ng_checksum_config_type,
122303612Sjulian		NULL
123303612Sjulian	},
124303612Sjulian	{
125303612Sjulian		NGM_CHECKSUM_COOKIE,
126303612Sjulian		NGM_CHECKSUM_GET_STATS,
127303612Sjulian		"getstats",
128303612Sjulian		NULL,
129303612Sjulian		&ng_checksum_stats_type
130303612Sjulian	},
131303612Sjulian	{
132303612Sjulian		NGM_CHECKSUM_COOKIE,
133303612Sjulian		NGM_CHECKSUM_CLR_STATS,
134303612Sjulian		"clrstats",
135303612Sjulian		NULL,
136303612Sjulian		NULL
137303612Sjulian	},
138303612Sjulian	{
139303612Sjulian		NGM_CHECKSUM_COOKIE,
140303612Sjulian		NGM_CHECKSUM_GETCLR_STATS,
141303612Sjulian		"getclrstats",
142303612Sjulian		NULL,
143303612Sjulian		&ng_checksum_stats_type
144303612Sjulian	},
145303612Sjulian	{ 0 }
146303612Sjulian};
147303612Sjulian
148303612Sjulianstatic struct ng_type typestruct = {
149303612Sjulian	.version =	NG_ABI_VERSION,
150303612Sjulian	.name =		NG_CHECKSUM_NODE_TYPE,
151303612Sjulian	.constructor =	ng_checksum_constructor,
152303612Sjulian	.rcvmsg =	ng_checksum_rcvmsg,
153303612Sjulian	.shutdown =	ng_checksum_shutdown,
154303612Sjulian	.newhook =	ng_checksum_newhook,
155303612Sjulian	.rcvdata =	ng_checksum_rcvdata,
156303612Sjulian	.disconnect =	ng_checksum_disconnect,
157303612Sjulian	.cmdlist =	ng_checksum_cmdlist,
158303612Sjulian};
159303612Sjulian
160303612SjulianNETGRAPH_INIT(checksum, &typestruct);
161303612Sjulian
162303612Sjulianstatic int
163303612Sjulianng_checksum_constructor(node_p node)
164303612Sjulian{
165303612Sjulian	priv_p priv;
166303612Sjulian
167303612Sjulian	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK|M_ZERO);
168303612Sjulian	priv->dlt = DLT_RAW;
169303612Sjulian
170303612Sjulian	NG_NODE_SET_PRIVATE(node, priv);
171303612Sjulian
172303612Sjulian	return (0);
173303612Sjulian}
174303612Sjulian
175303612Sjulianstatic int
176303612Sjulianng_checksum_newhook(node_p node, hook_p hook, const char *name)
177303612Sjulian{
178303612Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
179303612Sjulian
180303612Sjulian	if (strncmp(name, NG_CHECKSUM_HOOK_IN, strlen(NG_CHECKSUM_HOOK_IN)) == 0) {
181303612Sjulian		priv->in = hook;
182303612Sjulian	} else if (strncmp(name, NG_CHECKSUM_HOOK_OUT, strlen(NG_CHECKSUM_HOOK_OUT)) == 0) {
183303612Sjulian		priv->out = hook;
184303612Sjulian	} else
185303612Sjulian		return (EINVAL);
186303612Sjulian
187303612Sjulian	return (0);
188303612Sjulian}
189303612Sjulian
190303612Sjulianstatic int
191303612Sjulianng_checksum_rcvmsg(node_p node, item_p item, hook_p lasthook)
192303612Sjulian{
193303612Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
194303612Sjulian	struct ng_checksum_config *conf, *newconf;
195303612Sjulian	struct ng_mesg *msg;
196303612Sjulian	struct ng_mesg *resp = NULL;
197303612Sjulian	int error = 0;
198303612Sjulian
199303612Sjulian	NGI_GET_MSG(item, msg);
200303612Sjulian
201303612Sjulian	if  (msg->header.typecookie != NGM_CHECKSUM_COOKIE)
202303612Sjulian		ERROUT(EINVAL);
203303612Sjulian
204303612Sjulian	switch (msg->header.cmd)
205303612Sjulian	{
206303612Sjulian		case NGM_CHECKSUM_GETDLT:
207303612Sjulian			NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
208303612Sjulian
209303612Sjulian			if (resp == NULL)
210303612Sjulian				ERROUT(ENOMEM);
211303612Sjulian
212303612Sjulian			*((uint8_t *) resp->data) = priv->dlt;
213303612Sjulian
214303612Sjulian			break;
215303612Sjulian
216303612Sjulian		case NGM_CHECKSUM_SETDLT:
217303612Sjulian			if (msg->header.arglen != sizeof(uint8_t))
218303612Sjulian				ERROUT(EINVAL);
219303612Sjulian
220303612Sjulian			switch (*(uint8_t *) msg->data)
221303612Sjulian			{
222303612Sjulian				case DLT_EN10MB:
223303612Sjulian				case DLT_RAW:
224303612Sjulian					priv->dlt = *(uint8_t *) msg->data;
225303612Sjulian					break;
226303612Sjulian
227303612Sjulian				default:
228303612Sjulian					ERROUT(EINVAL);
229303612Sjulian			}
230303612Sjulian
231303612Sjulian			break;
232303612Sjulian
233303612Sjulian		case NGM_CHECKSUM_GETCONFIG:
234303612Sjulian			if (priv->conf == NULL)
235303612Sjulian				ERROUT(0);
236303612Sjulian
237303612Sjulian			NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_config), M_WAITOK);
238303612Sjulian
239303612Sjulian			if (resp == NULL)
240303612Sjulian				ERROUT(ENOMEM);
241303612Sjulian
242303612Sjulian			bcopy(priv->conf, resp->data, sizeof(struct ng_checksum_config));
243303612Sjulian
244303612Sjulian			break;
245303612Sjulian
246303612Sjulian		case NGM_CHECKSUM_SETCONFIG:
247303612Sjulian			conf = (struct ng_checksum_config *) msg->data;
248303612Sjulian
249303612Sjulian			if (msg->header.arglen != sizeof(struct ng_checksum_config))
250303612Sjulian				ERROUT(EINVAL);
251303612Sjulian
252303612Sjulian			conf->csum_flags &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
253303612Sjulian			conf->csum_offload &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
254303612Sjulian
255303612Sjulian			newconf = malloc(sizeof(struct ng_checksum_config), M_NETGRAPH, M_WAITOK|M_ZERO);
256303612Sjulian
257303612Sjulian			bcopy(conf, newconf, sizeof(struct ng_checksum_config));
258303612Sjulian
259303612Sjulian			if (priv->conf)
260303612Sjulian				free(priv->conf, M_NETGRAPH);
261303612Sjulian
262303612Sjulian			priv->conf = newconf;
263303612Sjulian
264303612Sjulian			break;
265303612Sjulian
266303612Sjulian		case NGM_CHECKSUM_GET_STATS:
267303612Sjulian		case NGM_CHECKSUM_CLR_STATS:
268303612Sjulian		case NGM_CHECKSUM_GETCLR_STATS:
269303612Sjulian			if (msg->header.cmd != NGM_CHECKSUM_CLR_STATS) {
270303612Sjulian				NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_stats), M_WAITOK);
271303612Sjulian
272303612Sjulian				if (resp == NULL)
273303612Sjulian					ERROUT(ENOMEM);
274303612Sjulian
275303612Sjulian				bcopy(&(priv->stats), resp->data, sizeof(struct ng_checksum_stats));
276303612Sjulian			}
277303612Sjulian
278303612Sjulian			if (msg->header.cmd != NGM_CHECKSUM_GET_STATS)
279303612Sjulian				bzero(&(priv->stats), sizeof(struct ng_checksum_stats));
280303612Sjulian
281303612Sjulian			break;
282303612Sjulian
283303612Sjulian		default:
284303612Sjulian			ERROUT(EINVAL);
285303612Sjulian	}
286303612Sjulian
287303612Sjuliandone:
288303612Sjulian	NG_RESPOND_MSG(error, node, item, resp);
289303612Sjulian	NG_FREE_MSG(msg);
290303612Sjulian
291303612Sjulian	return (error);
292303612Sjulian}
293303612Sjulian
294303612Sjulian#define	PULLUP_CHECK(mbuf, length) do {					\
295303612Sjulian	pullup_len += length;						\
296303612Sjulian	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
297303612Sjulian	    (pullup_len > MHLEN)) {					\
298303612Sjulian		return (EINVAL);					\
299303612Sjulian	}								\
300303612Sjulian	if ((mbuf)->m_len < pullup_len &&				\
301303612Sjulian	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
302303612Sjulian		return (ENOBUFS);					\
303303612Sjulian	}								\
304303612Sjulian} while (0)
305303612Sjulian
306303612Sjulian#ifdef INET
307303612Sjulianstatic int
308303612Sjulianchecksum_ipv4(priv_p priv, struct mbuf *m, int l3_offset)
309303612Sjulian{
310303612Sjulian	struct ip *ip4;
311303612Sjulian	int pullup_len;
312303612Sjulian	int hlen, plen;
313303612Sjulian	int processed = 0;
314303612Sjulian
315303612Sjulian	pullup_len = l3_offset;
316303612Sjulian
317303612Sjulian	PULLUP_CHECK(m, sizeof(struct ip));
318303612Sjulian	ip4 = (struct ip *) mtodo(m, l3_offset);
319303612Sjulian
320303612Sjulian	if (ip4->ip_v != IPVERSION)
321303612Sjulian		return (EOPNOTSUPP);
322303612Sjulian
323303612Sjulian	hlen = ip4->ip_hl << 2;
324303612Sjulian	plen = ntohs(ip4->ip_len);
325303612Sjulian
326303612Sjulian	if (hlen < sizeof(struct ip) || m->m_pkthdr.len < l3_offset + plen)
327303612Sjulian		return (EINVAL);
328303612Sjulian
329303612Sjulian	if (m->m_pkthdr.csum_flags & CSUM_IP) {
330303612Sjulian		ip4->ip_sum = 0;
331303612Sjulian
332303612Sjulian		if ((priv->conf->csum_offload & CSUM_IP) == 0) {
333303612Sjulian			if (hlen == sizeof(struct ip))
334303612Sjulian				ip4->ip_sum = in_cksum_hdr(ip4);
335303612Sjulian			else
336303612Sjulian				ip4->ip_sum = in_cksum_skip(m, l3_offset + hlen, l3_offset);
337303612Sjulian
338303612Sjulian			m->m_pkthdr.csum_flags &= ~CSUM_IP;
339303612Sjulian		}
340303612Sjulian
341303612Sjulian		processed = 1;
342303612Sjulian	}
343303612Sjulian
344303612Sjulian	pullup_len = l3_offset + hlen;
345303612Sjulian
346303612Sjulian	/* We can not calculate a checksum fragmented packets */
347303612Sjulian	if (ip4->ip_off & htons(IP_MF|IP_OFFMASK)) {
348303612Sjulian		m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
349303612Sjulian		return (0);
350303612Sjulian	}
351303612Sjulian
352303612Sjulian	switch (ip4->ip_p)
353303612Sjulian	{
354303612Sjulian		case IPPROTO_TCP:
355303612Sjulian			if (m->m_pkthdr.csum_flags & CSUM_TCP) {
356303612Sjulian				struct tcphdr *th;
357303612Sjulian
358303612Sjulian				PULLUP_CHECK(m, sizeof(struct tcphdr));
359303612Sjulian				th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
360303612Sjulian
361303612Sjulian				th->th_sum = in_pseudo(ip4->ip_src.s_addr,
362303612Sjulian				    ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
363303612Sjulian
364303612Sjulian				if ((priv->conf->csum_offload & CSUM_TCP) == 0) {
365303612Sjulian					th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
366303612Sjulian					m->m_pkthdr.csum_flags &= ~CSUM_TCP;
367303612Sjulian				}
368303612Sjulian
369303612Sjulian				processed = 1;
370303612Sjulian			}
371303612Sjulian
372303612Sjulian			m->m_pkthdr.csum_flags &= ~CSUM_UDP;
373303612Sjulian			break;
374303612Sjulian
375303612Sjulian		case IPPROTO_UDP:
376303612Sjulian			if (m->m_pkthdr.csum_flags & CSUM_UDP) {
377303612Sjulian				struct udphdr *uh;
378303612Sjulian
379303612Sjulian				PULLUP_CHECK(m, sizeof(struct udphdr));
380303612Sjulian				uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
381303612Sjulian
382303612Sjulian				uh->uh_sum = in_pseudo(ip4->ip_src.s_addr,
383303612Sjulian				    ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
384303612Sjulian
385303612Sjulian				if ((priv->conf->csum_offload & CSUM_UDP) == 0) {
386303612Sjulian					uh->uh_sum = in_cksum_skip(m,
387303612Sjulian					    l3_offset + plen, l3_offset + hlen);
388303612Sjulian
389303612Sjulian					if (uh->uh_sum == 0)
390303612Sjulian						uh->uh_sum = 0xffff;
391303612Sjulian
392303612Sjulian					m->m_pkthdr.csum_flags &= ~CSUM_UDP;
393303612Sjulian				}
394303612Sjulian
395303612Sjulian				processed = 1;
396303612Sjulian			}
397303612Sjulian
398303612Sjulian			m->m_pkthdr.csum_flags &= ~CSUM_TCP;
399303612Sjulian			break;
400303612Sjulian
401303612Sjulian		default:
402303612Sjulian			m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
403303612Sjulian			break;
404303612Sjulian	}
405303612Sjulian
406303612Sjulian	m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV6;
407303612Sjulian
408303612Sjulian	if (processed)
409303612Sjulian		priv->stats.processed++;
410303612Sjulian
411303612Sjulian	return (0);
412303612Sjulian}
413303612Sjulian#endif /* INET */
414303612Sjulian
415303612Sjulian#ifdef INET6
416303612Sjulianstatic int
417303612Sjulianchecksum_ipv6(priv_p priv, struct mbuf *m, int l3_offset)
418303612Sjulian{
419303612Sjulian	struct ip6_hdr *ip6;
420303612Sjulian	struct ip6_ext *ip6e = NULL;
421303612Sjulian	int pullup_len;
422303612Sjulian	int hlen, plen;
423303612Sjulian	int nxt;
424303612Sjulian	int processed = 0;
425303612Sjulian
426303612Sjulian	pullup_len = l3_offset;
427303612Sjulian
428303612Sjulian	PULLUP_CHECK(m, sizeof(struct ip6_hdr));
429303612Sjulian	ip6 = (struct ip6_hdr *) mtodo(m, l3_offset);
430303612Sjulian
431303612Sjulian	if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
432303612Sjulian		return (EOPNOTSUPP);
433303612Sjulian
434303612Sjulian	hlen = sizeof(struct ip6_hdr);
435303612Sjulian	plen = ntohs(ip6->ip6_plen) + hlen;
436303612Sjulian
437303612Sjulian	if (m->m_pkthdr.len < l3_offset + plen)
438303612Sjulian		return (EINVAL);
439303612Sjulian
440303612Sjulian	nxt = ip6->ip6_nxt;
441303612Sjulian
442303612Sjulian	for (;;) {
443303612Sjulian		switch (nxt)
444303612Sjulian		{
445303612Sjulian			case IPPROTO_DSTOPTS:
446303612Sjulian			case IPPROTO_HOPOPTS:
447303612Sjulian			case IPPROTO_ROUTING:
448303612Sjulian				PULLUP_CHECK(m, sizeof(struct ip6_ext));
449303612Sjulian				ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
450303612Sjulian				nxt = ip6e->ip6e_nxt;
451303612Sjulian				hlen += (ip6e->ip6e_len + 1) << 3;
452303612Sjulian				pullup_len = l3_offset + hlen;
453303612Sjulian				break;
454303612Sjulian
455303612Sjulian			case IPPROTO_AH:
456303612Sjulian				PULLUP_CHECK(m, sizeof(struct ip6_ext));
457303612Sjulian				ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
458303612Sjulian				nxt = ip6e->ip6e_nxt;
459303612Sjulian				hlen += (ip6e->ip6e_len + 2) << 2;
460303612Sjulian				pullup_len = l3_offset + hlen;
461303612Sjulian				break;
462303612Sjulian
463303612Sjulian			case IPPROTO_FRAGMENT:
464303612Sjulian				/* We can not calculate a checksum fragmented packets */
465303612Sjulian				m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
466303612Sjulian				return (0);
467303612Sjulian
468303612Sjulian			default:
469303612Sjulian				goto loopend;
470303612Sjulian		}
471303612Sjulian
472303612Sjulian		if (nxt == 0)
473303612Sjulian			return (EINVAL);
474303612Sjulian	}
475303612Sjulian
476303612Sjulianloopend:
477303612Sjulian
478303612Sjulian	switch (nxt)
479303612Sjulian	{
480303612Sjulian		case IPPROTO_TCP:
481303612Sjulian			if (m->m_pkthdr.csum_flags & CSUM_TCP_IPV6) {
482303612Sjulian				struct tcphdr *th;
483303612Sjulian
484303612Sjulian				PULLUP_CHECK(m, sizeof(struct tcphdr));
485303612Sjulian				th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
486303612Sjulian
487303612Sjulian				th->th_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
488303612Sjulian
489303612Sjulian				if ((priv->conf->csum_offload & CSUM_TCP_IPV6) == 0) {
490303612Sjulian					th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
491303612Sjulian					m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
492303612Sjulian				}
493303612Sjulian
494303612Sjulian				processed = 1;
495303612Sjulian			}
496303612Sjulian
497303612Sjulian			m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
498303612Sjulian			break;
499303612Sjulian
500303612Sjulian		case IPPROTO_UDP:
501303612Sjulian			if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6) {
502303612Sjulian				struct udphdr *uh;
503303612Sjulian
504303612Sjulian				PULLUP_CHECK(m, sizeof(struct udphdr));
505303612Sjulian				uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
506303612Sjulian
507303612Sjulian				uh->uh_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
508303612Sjulian
509303612Sjulian				if ((priv->conf->csum_offload & CSUM_UDP_IPV6) == 0) {
510303612Sjulian					uh->uh_sum = in_cksum_skip(m,
511303612Sjulian					    l3_offset + plen, l3_offset + hlen);
512303612Sjulian
513303612Sjulian					if (uh->uh_sum == 0)
514303612Sjulian						uh->uh_sum = 0xffff;
515303612Sjulian
516303612Sjulian					m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
517303612Sjulian				}
518303612Sjulian
519303612Sjulian				processed = 1;
520303612Sjulian			}
521303612Sjulian
522303612Sjulian			m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
523303612Sjulian			break;
524303612Sjulian
525303612Sjulian		default:
526303612Sjulian			m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
527303612Sjulian			break;
528303612Sjulian	}
529303612Sjulian
530303612Sjulian	m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV4;
531303612Sjulian
532303612Sjulian	if (processed)
533303612Sjulian		priv->stats.processed++;
534303612Sjulian
535303612Sjulian	return (0);
536303612Sjulian}
537303612Sjulian#endif /* INET6 */
538303612Sjulian
539303612Sjulian#undef	PULLUP_CHECK
540303612Sjulian
541303612Sjulianstatic int
542303612Sjulianng_checksum_rcvdata(hook_p hook, item_p item)
543303612Sjulian{
544303612Sjulian	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
545303612Sjulian	struct mbuf *m;
546303612Sjulian	hook_p out;
547303612Sjulian	int error = 0;
548303612Sjulian
549303612Sjulian	priv->stats.received++;
550303612Sjulian
551303612Sjulian	NGI_GET_M(item, m);
552303612Sjulian
553303612Sjulian#define	PULLUP_CHECK(mbuf, length) do {					\
554303612Sjulian	pullup_len += length;						\
555303612Sjulian	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
556303612Sjulian	    (pullup_len > MHLEN)) {					\
557303612Sjulian		error = EINVAL;						\
558303612Sjulian		goto bypass;						\
559303612Sjulian	}								\
560303612Sjulian	if ((mbuf)->m_len < pullup_len &&				\
561303612Sjulian	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
562303612Sjulian		error = ENOBUFS;					\
563303612Sjulian		goto drop;						\
564303612Sjulian	}								\
565303612Sjulian} while (0)
566303612Sjulian
567303612Sjulian	if (!(priv->conf && hook == priv->in && m && (m->m_flags & M_PKTHDR)))
568303612Sjulian		goto bypass;
569303612Sjulian
570303612Sjulian	m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
571303612Sjulian
572303612Sjulian	if (m->m_pkthdr.csum_flags & (NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6))
573303612Sjulian	{
574303612Sjulian		struct ether_header *eh;
575303612Sjulian		struct ng_checksum_vlan_header *vh;
576303612Sjulian		int pullup_len = 0;
577303612Sjulian		uint16_t etype;
578303612Sjulian
579303612Sjulian		m = m_unshare(m, M_NOWAIT);
580303612Sjulian
581303612Sjulian		if (m == NULL)
582303612Sjulian			ERROUT(ENOMEM);
583303612Sjulian
584303612Sjulian		switch (priv->dlt)
585303612Sjulian		{
586303612Sjulian			case DLT_EN10MB:
587303612Sjulian				PULLUP_CHECK(m, sizeof(struct ether_header));
588303612Sjulian				eh = mtod(m, struct ether_header *);
589303612Sjulian				etype = ntohs(eh->ether_type);
590303612Sjulian
591303612Sjulian				for (;;) {	/* QinQ support */
592303612Sjulian					switch (etype)
593303612Sjulian					{
594303612Sjulian						case 0x8100:
595303612Sjulian						case 0x88A8:
596303612Sjulian						case 0x9100:
597303612Sjulian							PULLUP_CHECK(m, sizeof(struct ng_checksum_vlan_header));
598303612Sjulian							vh = (struct ng_checksum_vlan_header *) mtodo(m,
599303612Sjulian							    pullup_len - sizeof(struct ng_checksum_vlan_header));
600303612Sjulian							etype = ntohs(vh->etype);
601303612Sjulian							break;
602303612Sjulian
603303612Sjulian						default:
604303612Sjulian							goto loopend;
605303612Sjulian					}
606303612Sjulian				}
607303612Sjulianloopend:
608303612Sjulian#ifdef INET
609303612Sjulian				if (etype == ETHERTYPE_IP &&
610303612Sjulian				    (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)) {
611303612Sjulian					error = checksum_ipv4(priv, m, pullup_len);
612303612Sjulian					if (error == ENOBUFS)
613303612Sjulian						goto drop;
614303612Sjulian				} else
615303612Sjulian#endif
616303612Sjulian#ifdef INET6
617303612Sjulian				if (etype == ETHERTYPE_IPV6 &&
618303612Sjulian				    (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)) {
619303612Sjulian					error = checksum_ipv6(priv, m, pullup_len);
620303612Sjulian					if (error == ENOBUFS)
621303612Sjulian						goto drop;
622303612Sjulian				} else
623303612Sjulian#endif
624303612Sjulian				{
625303612Sjulian					m->m_pkthdr.csum_flags &=
626303612Sjulian					    ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
627303612Sjulian				}
628303612Sjulian
629303612Sjulian				break;
630303612Sjulian
631303612Sjulian			case DLT_RAW:
632303612Sjulian#ifdef INET
633303612Sjulian				if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)
634303612Sjulian				{
635303612Sjulian					error = checksum_ipv4(priv, m, pullup_len);
636303612Sjulian
637303612Sjulian					if (error == 0)
638303612Sjulian						goto bypass;
639303612Sjulian					else if (error == ENOBUFS)
640303612Sjulian						goto drop;
641303612Sjulian				}
642303612Sjulian#endif
643303612Sjulian#ifdef INET6
644303612Sjulian				if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)
645303612Sjulian				{
646303612Sjulian					error = checksum_ipv6(priv, m, pullup_len);
647303612Sjulian
648303612Sjulian					if (error == 0)
649303612Sjulian						goto bypass;
650303612Sjulian					else if (error == ENOBUFS)
651303612Sjulian						goto drop;
652303612Sjulian				}
653303612Sjulian#endif
654303612Sjulian				if (error)
655303612Sjulian					m->m_pkthdr.csum_flags &=
656303612Sjulian					    ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
657303612Sjulian
658303612Sjulian				break;
659303612Sjulian
660303612Sjulian			default:
661303612Sjulian				ERROUT(EINVAL);
662303612Sjulian		}
663303612Sjulian	}
664303612Sjulian
665303612Sjulian#undef	PULLUP_CHECK
666303612Sjulian
667303612Sjulianbypass:
668303612Sjulian	out = NULL;
669303612Sjulian
670303612Sjulian	if (hook == priv->in) {
671303612Sjulian		/* return frames on 'in' hook if 'out' not connected */
672303612Sjulian		out = priv->out ? priv->out : priv->in;
673303612Sjulian	} else if (hook == priv->out && priv->in) {
674303612Sjulian		/* pass frames on 'out' hook if 'in' connected */
675303612Sjulian		out = priv->in;
676303612Sjulian	}
677303612Sjulian
678303612Sjulian	if (out == NULL)
679303612Sjulian		ERROUT(0);
680303612Sjulian
681303612Sjulian	NG_FWD_NEW_DATA(error, item, out, m);
682303612Sjulian
683303612Sjulian	return (error);
684303612Sjulian
685303612Sjuliandone:
686303612Sjuliandrop:
687303612Sjulian	NG_FREE_ITEM(item);
688303612Sjulian	NG_FREE_M(m);
689303612Sjulian
690303612Sjulian	priv->stats.dropped++;
691303612Sjulian
692303612Sjulian	return (error);
693303612Sjulian}
694303612Sjulian
695303612Sjulianstatic int
696303612Sjulianng_checksum_shutdown(node_p node)
697303612Sjulian{
698303612Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
699303612Sjulian
700303612Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
701303612Sjulian	NG_NODE_UNREF(node);
702303612Sjulian
703303612Sjulian	if (priv->conf)
704303612Sjulian		free(priv->conf, M_NETGRAPH);
705303612Sjulian
706303612Sjulian	free(priv, M_NETGRAPH);
707303612Sjulian
708303612Sjulian	return (0);
709303612Sjulian}
710303612Sjulian
711303612Sjulianstatic int
712303612Sjulianng_checksum_disconnect(hook_p hook)
713303612Sjulian{
714303612Sjulian	priv_p priv;
715303612Sjulian
716303612Sjulian	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
717303612Sjulian
718303612Sjulian	if (hook == priv->in)
719303612Sjulian		priv->in = NULL;
720303612Sjulian
721303612Sjulian	if (hook == priv->out)
722303612Sjulian		priv->out = NULL;
723303612Sjulian
724303612Sjulian	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
725303612Sjulian	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
726303612Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
727303612Sjulian
728303612Sjulian	return (0);
729303612Sjulian}
730