xref: /dragonfly/sys/netgraph7/tcpmss/ng_tcpmss.c (revision 0a66de84)
1*0a66de84SNuno Antunes /*-
2*0a66de84SNuno Antunes  * ng_tcpmss.c
3*0a66de84SNuno Antunes  *
4*0a66de84SNuno Antunes  * Copyright (c) 2004, Alexey Popov <lollypop@flexuser.ru>
5*0a66de84SNuno Antunes  * All rights reserved.
6*0a66de84SNuno Antunes  *
7*0a66de84SNuno Antunes  * Redistribution and use in source and binary forms, with or without
8*0a66de84SNuno Antunes  * modification, are permitted provided that the following conditions
9*0a66de84SNuno Antunes  * are met:
10*0a66de84SNuno Antunes  * 1. Redistributions of source code must retain the above copyright
11*0a66de84SNuno Antunes  *    notice unmodified, this list of conditions, and the following
12*0a66de84SNuno Antunes  *    disclaimer.
13*0a66de84SNuno Antunes  * 2. Redistributions in binary form must reproduce the above copyright
14*0a66de84SNuno Antunes  *    notice, this list of conditions and the following disclaimer in the
15*0a66de84SNuno Antunes  *    documentation and/or other materials provided with the distribution.
16*0a66de84SNuno Antunes  *
17*0a66de84SNuno Antunes  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*0a66de84SNuno Antunes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*0a66de84SNuno Antunes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*0a66de84SNuno Antunes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*0a66de84SNuno Antunes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*0a66de84SNuno Antunes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*0a66de84SNuno Antunes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*0a66de84SNuno Antunes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*0a66de84SNuno Antunes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*0a66de84SNuno Antunes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*0a66de84SNuno Antunes  * SUCH DAMAGE.
28*0a66de84SNuno Antunes  *
29*0a66de84SNuno Antunes  * This software includes fragments of the following programs:
30*0a66de84SNuno Antunes  *	tcpmssd		Ruslan Ermilov <ru@FreeBSD.org>
31*0a66de84SNuno Antunes  *
32*0a66de84SNuno Antunes  * $FreeBSD: src/sys/netgraph/ng_tcpmss.c,v 1.4 2007/01/15 05:01:31 glebius Exp $
33*0a66de84SNuno Antunes  * $DragonFly: src/sys/netgraph7/ng_tcpmss.c,v 1.2 2008/06/26 23:05:35 dillon Exp $
34*0a66de84SNuno Antunes  */
35*0a66de84SNuno Antunes 
36*0a66de84SNuno Antunes /*
37*0a66de84SNuno Antunes  * This node is netgraph tool for workaround of PMTUD problem. It acts
38*0a66de84SNuno Antunes  * like filter for IP packets. If configured, it reduces MSS of TCP SYN
39*0a66de84SNuno Antunes  * packets.
40*0a66de84SNuno Antunes  *
41*0a66de84SNuno Antunes  * Configuration can be done by sending NGM_TCPMSS_CONFIG message. The
42*0a66de84SNuno Antunes  * message sets filter for incoming packets on hook 'inHook'. Packet's
43*0a66de84SNuno Antunes  * TCP MSS field is lowered to 'maxMSS' parameter and resulting packet
44*0a66de84SNuno Antunes  * is sent to 'outHook'.
45*0a66de84SNuno Antunes  *
46*0a66de84SNuno Antunes  * XXX: statistics are updated not atomically, so they may broke on SMP.
47*0a66de84SNuno Antunes  */
48*0a66de84SNuno Antunes 
49*0a66de84SNuno Antunes #include <sys/param.h>
50*0a66de84SNuno Antunes #include <sys/systm.h>
51*0a66de84SNuno Antunes #include <sys/errno.h>
52*0a66de84SNuno Antunes #include <sys/kernel.h>
53*0a66de84SNuno Antunes #include <sys/malloc.h>
54*0a66de84SNuno Antunes #include <sys/mbuf.h>
55*0a66de84SNuno Antunes 
56*0a66de84SNuno Antunes #include <netinet/in.h>
57*0a66de84SNuno Antunes #include <netinet/in_systm.h>
58*0a66de84SNuno Antunes #include <netinet/ip.h>
59*0a66de84SNuno Antunes #include <netinet/tcp.h>
60*0a66de84SNuno Antunes 
61*0a66de84SNuno Antunes #include <netgraph7/ng_message.h>
62*0a66de84SNuno Antunes #include <netgraph7/netgraph.h>
63*0a66de84SNuno Antunes #include <netgraph7/ng_parse.h>
64*0a66de84SNuno Antunes #include "ng_tcpmss.h"
65*0a66de84SNuno Antunes 
66*0a66de84SNuno Antunes /* Per hook info. */
67*0a66de84SNuno Antunes typedef struct {
68*0a66de84SNuno Antunes 	hook_p				outHook;
69*0a66de84SNuno Antunes 	struct ng_tcpmss_hookstat	stats;
70*0a66de84SNuno Antunes } *hpriv_p;
71*0a66de84SNuno Antunes 
72*0a66de84SNuno Antunes /* Netgraph methods. */
73*0a66de84SNuno Antunes static ng_constructor_t	ng_tcpmss_constructor;
74*0a66de84SNuno Antunes static ng_rcvmsg_t	ng_tcpmss_rcvmsg;
75*0a66de84SNuno Antunes static ng_newhook_t	ng_tcpmss_newhook;
76*0a66de84SNuno Antunes static ng_rcvdata_t	ng_tcpmss_rcvdata;
77*0a66de84SNuno Antunes static ng_disconnect_t	ng_tcpmss_disconnect;
78*0a66de84SNuno Antunes 
79*0a66de84SNuno Antunes static int correct_mss(struct tcphdr *, int, uint16_t, int);
80*0a66de84SNuno Antunes 
81*0a66de84SNuno Antunes /* Parse type for struct ng_tcpmss_hookstat. */
82*0a66de84SNuno Antunes static const struct ng_parse_struct_field ng_tcpmss_hookstat_type_fields[]
83*0a66de84SNuno Antunes 	= NG_TCPMSS_HOOKSTAT_INFO;
84*0a66de84SNuno Antunes static const struct ng_parse_type ng_tcpmss_hookstat_type = {
85*0a66de84SNuno Antunes 	&ng_parse_struct_type,
86*0a66de84SNuno Antunes 	&ng_tcpmss_hookstat_type_fields
87*0a66de84SNuno Antunes };
88*0a66de84SNuno Antunes 
89*0a66de84SNuno Antunes /* Parse type for struct ng_tcpmss_config. */
90*0a66de84SNuno Antunes static const struct ng_parse_struct_field ng_tcpmss_config_type_fields[]
91*0a66de84SNuno Antunes 	= NG_TCPMSS_CONFIG_INFO;
92*0a66de84SNuno Antunes static const struct ng_parse_type ng_tcpmss_config_type = {
93*0a66de84SNuno Antunes 	&ng_parse_struct_type,
94*0a66de84SNuno Antunes 	ng_tcpmss_config_type_fields
95*0a66de84SNuno Antunes };
96*0a66de84SNuno Antunes 
97*0a66de84SNuno Antunes /* List of commands and how to convert arguments to/from ASCII. */
98*0a66de84SNuno Antunes static const struct ng_cmdlist ng_tcpmss_cmds[] = {
99*0a66de84SNuno Antunes 	{
100*0a66de84SNuno Antunes 	  NGM_TCPMSS_COOKIE,
101*0a66de84SNuno Antunes 	  NGM_TCPMSS_GET_STATS,
102*0a66de84SNuno Antunes 	  "getstats",
103*0a66de84SNuno Antunes 	  &ng_parse_hookbuf_type,
104*0a66de84SNuno Antunes 	  &ng_tcpmss_hookstat_type
105*0a66de84SNuno Antunes 	},
106*0a66de84SNuno Antunes 	{
107*0a66de84SNuno Antunes 	  NGM_TCPMSS_COOKIE,
108*0a66de84SNuno Antunes 	  NGM_TCPMSS_CLR_STATS,
109*0a66de84SNuno Antunes 	  "clrstats",
110*0a66de84SNuno Antunes 	  &ng_parse_hookbuf_type,
111*0a66de84SNuno Antunes 	  NULL
112*0a66de84SNuno Antunes 	},
113*0a66de84SNuno Antunes 	{
114*0a66de84SNuno Antunes 	  NGM_TCPMSS_COOKIE,
115*0a66de84SNuno Antunes 	  NGM_TCPMSS_GETCLR_STATS,
116*0a66de84SNuno Antunes 	  "getclrstats",
117*0a66de84SNuno Antunes 	  &ng_parse_hookbuf_type,
118*0a66de84SNuno Antunes 	  &ng_tcpmss_hookstat_type
119*0a66de84SNuno Antunes 	},
120*0a66de84SNuno Antunes 	{
121*0a66de84SNuno Antunes 	  NGM_TCPMSS_COOKIE,
122*0a66de84SNuno Antunes 	  NGM_TCPMSS_CONFIG,
123*0a66de84SNuno Antunes 	  "config",
124*0a66de84SNuno Antunes 	  &ng_tcpmss_config_type,
125*0a66de84SNuno Antunes 	  NULL
126*0a66de84SNuno Antunes 	},
127*0a66de84SNuno Antunes 	{ 0 }
128*0a66de84SNuno Antunes };
129*0a66de84SNuno Antunes 
130*0a66de84SNuno Antunes /* Netgraph type descriptor. */
131*0a66de84SNuno Antunes static struct ng_type ng_tcpmss_typestruct = {
132*0a66de84SNuno Antunes 	.version =	NG_ABI_VERSION,
133*0a66de84SNuno Antunes 	.name =		NG_TCPMSS_NODE_TYPE,
134*0a66de84SNuno Antunes 	.constructor =	ng_tcpmss_constructor,
135*0a66de84SNuno Antunes 	.rcvmsg =	ng_tcpmss_rcvmsg,
136*0a66de84SNuno Antunes 	.newhook =	ng_tcpmss_newhook,
137*0a66de84SNuno Antunes 	.rcvdata =	ng_tcpmss_rcvdata,
138*0a66de84SNuno Antunes 	.disconnect =	ng_tcpmss_disconnect,
139*0a66de84SNuno Antunes 	.cmdlist =	ng_tcpmss_cmds,
140*0a66de84SNuno Antunes };
141*0a66de84SNuno Antunes 
142*0a66de84SNuno Antunes NETGRAPH_INIT(tcpmss, &ng_tcpmss_typestruct);
143*0a66de84SNuno Antunes 
144*0a66de84SNuno Antunes #define	ERROUT(x)	{ error = (x); goto done; }
145*0a66de84SNuno Antunes 
146*0a66de84SNuno Antunes /*
147*0a66de84SNuno Antunes  * Node constructor. No special actions required.
148*0a66de84SNuno Antunes  */
149*0a66de84SNuno Antunes static int
ng_tcpmss_constructor(node_p node)150*0a66de84SNuno Antunes ng_tcpmss_constructor(node_p node)
151*0a66de84SNuno Antunes {
152*0a66de84SNuno Antunes 	return (0);
153*0a66de84SNuno Antunes }
154*0a66de84SNuno Antunes 
155*0a66de84SNuno Antunes /*
156*0a66de84SNuno Antunes  * Add a hook. Any unique name is OK.
157*0a66de84SNuno Antunes  */
158*0a66de84SNuno Antunes static int
ng_tcpmss_newhook(node_p node,hook_p hook,const char * name)159*0a66de84SNuno Antunes ng_tcpmss_newhook(node_p node, hook_p hook, const char *name)
160*0a66de84SNuno Antunes {
161*0a66de84SNuno Antunes 	hpriv_p priv;
162*0a66de84SNuno Antunes 
163*0a66de84SNuno Antunes 	priv = kmalloc(sizeof(*priv), M_NETGRAPH,
164*0a66de84SNuno Antunes 		       M_WAITOK | M_NULLOK | M_ZERO);
165*0a66de84SNuno Antunes 	if (priv == NULL)
166*0a66de84SNuno Antunes 		return (ENOMEM);
167*0a66de84SNuno Antunes 
168*0a66de84SNuno Antunes 	NG_HOOK_SET_PRIVATE(hook, priv);
169*0a66de84SNuno Antunes 
170*0a66de84SNuno Antunes 	return (0);
171*0a66de84SNuno Antunes }
172*0a66de84SNuno Antunes 
173*0a66de84SNuno Antunes /*
174*0a66de84SNuno Antunes  * Receive a control message.
175*0a66de84SNuno Antunes  */
176*0a66de84SNuno Antunes static int
ng_tcpmss_rcvmsg(node_p node,item_p item,hook_p lasthook)177*0a66de84SNuno Antunes ng_tcpmss_rcvmsg
178*0a66de84SNuno Antunes (node_p node, item_p item, hook_p lasthook)
179*0a66de84SNuno Antunes {
180*0a66de84SNuno Antunes 	struct ng_mesg *msg, *resp = NULL;
181*0a66de84SNuno Antunes 	int error = 0;
182*0a66de84SNuno Antunes 
183*0a66de84SNuno Antunes 	NGI_GET_MSG(item, msg);
184*0a66de84SNuno Antunes 
185*0a66de84SNuno Antunes 	switch (msg->header.typecookie) {
186*0a66de84SNuno Antunes 	case NGM_TCPMSS_COOKIE:
187*0a66de84SNuno Antunes 		switch (msg->header.cmd) {
188*0a66de84SNuno Antunes 		case NGM_TCPMSS_GET_STATS:
189*0a66de84SNuno Antunes 		case NGM_TCPMSS_CLR_STATS:
190*0a66de84SNuno Antunes 		case NGM_TCPMSS_GETCLR_STATS:
191*0a66de84SNuno Antunes 		    {
192*0a66de84SNuno Antunes 			hook_p hook;
193*0a66de84SNuno Antunes 			hpriv_p priv;
194*0a66de84SNuno Antunes 
195*0a66de84SNuno Antunes 			/* Check that message is long enough. */
196*0a66de84SNuno Antunes 			if (msg->header.arglen != NG_HOOKSIZ)
197*0a66de84SNuno Antunes 				ERROUT(EINVAL);
198*0a66de84SNuno Antunes 
199*0a66de84SNuno Antunes 			/* Find this hook. */
200*0a66de84SNuno Antunes 			hook = ng_findhook(node, (char *)msg->data);
201*0a66de84SNuno Antunes 			if (hook == NULL)
202*0a66de84SNuno Antunes 				ERROUT(ENOENT);
203*0a66de84SNuno Antunes 
204*0a66de84SNuno Antunes 			priv = NG_HOOK_PRIVATE(hook);
205*0a66de84SNuno Antunes 
206*0a66de84SNuno Antunes 			/* Create response. */
207*0a66de84SNuno Antunes 			if (msg->header.cmd != NGM_TCPMSS_CLR_STATS) {
208*0a66de84SNuno Antunes 				NG_MKRESPONSE(resp, msg,
209*0a66de84SNuno Antunes 				    sizeof(struct ng_tcpmss_hookstat), M_WAITOK | M_NULLOK);
210*0a66de84SNuno Antunes 				if (resp == NULL)
211*0a66de84SNuno Antunes 					ERROUT(ENOMEM);
212*0a66de84SNuno Antunes 				bcopy(&priv->stats, resp->data,
213*0a66de84SNuno Antunes 				    sizeof(struct ng_tcpmss_hookstat));
214*0a66de84SNuno Antunes 			}
215*0a66de84SNuno Antunes 
216*0a66de84SNuno Antunes 			if (msg->header.cmd != NGM_TCPMSS_GET_STATS)
217*0a66de84SNuno Antunes 				bzero(&priv->stats,
218*0a66de84SNuno Antunes 				    sizeof(struct ng_tcpmss_hookstat));
219*0a66de84SNuno Antunes 			break;
220*0a66de84SNuno Antunes 		    }
221*0a66de84SNuno Antunes 		case NGM_TCPMSS_CONFIG:
222*0a66de84SNuno Antunes 		    {
223*0a66de84SNuno Antunes 			struct ng_tcpmss_config *set;
224*0a66de84SNuno Antunes 			hook_p in, out;
225*0a66de84SNuno Antunes 			hpriv_p priv;
226*0a66de84SNuno Antunes 
227*0a66de84SNuno Antunes 			/* Check that message is long enough. */
228*0a66de84SNuno Antunes 			if (msg->header.arglen !=
229*0a66de84SNuno Antunes 			    sizeof(struct ng_tcpmss_config))
230*0a66de84SNuno Antunes 				ERROUT(EINVAL);
231*0a66de84SNuno Antunes 
232*0a66de84SNuno Antunes 			set = (struct ng_tcpmss_config *)msg->data;
233*0a66de84SNuno Antunes 			in = ng_findhook(node, set->inHook);
234*0a66de84SNuno Antunes 			out = ng_findhook(node, set->outHook);
235*0a66de84SNuno Antunes 			if (in == NULL || out == NULL)
236*0a66de84SNuno Antunes 				ERROUT(ENOENT);
237*0a66de84SNuno Antunes 
238*0a66de84SNuno Antunes 			/* Configure MSS hack. */
239*0a66de84SNuno Antunes 			priv = NG_HOOK_PRIVATE(in);
240*0a66de84SNuno Antunes 			priv->outHook = out;
241*0a66de84SNuno Antunes 			priv->stats.maxMSS = set->maxMSS;
242*0a66de84SNuno Antunes 
243*0a66de84SNuno Antunes 			break;
244*0a66de84SNuno Antunes  		    }
245*0a66de84SNuno Antunes 		default:
246*0a66de84SNuno Antunes 			error = EINVAL;
247*0a66de84SNuno Antunes 			break;
248*0a66de84SNuno Antunes 		}
249*0a66de84SNuno Antunes 		break;
250*0a66de84SNuno Antunes 	default:
251*0a66de84SNuno Antunes 		error = EINVAL;
252*0a66de84SNuno Antunes 		break;
253*0a66de84SNuno Antunes 	}
254*0a66de84SNuno Antunes 
255*0a66de84SNuno Antunes done:
256*0a66de84SNuno Antunes 	NG_RESPOND_MSG(error, node, item, resp);
257*0a66de84SNuno Antunes 	NG_FREE_MSG(msg);
258*0a66de84SNuno Antunes 
259*0a66de84SNuno Antunes 	return (error);
260*0a66de84SNuno Antunes }
261*0a66de84SNuno Antunes 
262*0a66de84SNuno Antunes /*
263*0a66de84SNuno Antunes  * Receive data on a hook, and hack MSS.
264*0a66de84SNuno Antunes  *
265*0a66de84SNuno Antunes  */
266*0a66de84SNuno Antunes static int
ng_tcpmss_rcvdata(hook_p hook,item_p item)267*0a66de84SNuno Antunes ng_tcpmss_rcvdata(hook_p hook, item_p item)
268*0a66de84SNuno Antunes {
269*0a66de84SNuno Antunes 	hpriv_p priv = NG_HOOK_PRIVATE(hook);
270*0a66de84SNuno Antunes 	struct mbuf *m = NULL;
271*0a66de84SNuno Antunes 	struct ip *ip;
272*0a66de84SNuno Antunes 	struct tcphdr *tcp;
273*0a66de84SNuno Antunes 	int iphlen, tcphlen, pktlen;
274*0a66de84SNuno Antunes 	int pullup_len = 0;
275*0a66de84SNuno Antunes 	int error = 0;
276*0a66de84SNuno Antunes 
277*0a66de84SNuno Antunes 	/* Drop packets if filter is not configured on this hook. */
278*0a66de84SNuno Antunes 	if (priv->outHook == NULL)
279*0a66de84SNuno Antunes 		goto done;
280*0a66de84SNuno Antunes 
281*0a66de84SNuno Antunes 	NGI_GET_M(item, m);
282*0a66de84SNuno Antunes 
283*0a66de84SNuno Antunes 	/* Update stats on incoming hook. */
284*0a66de84SNuno Antunes 	pktlen = m->m_pkthdr.len;
285*0a66de84SNuno Antunes 	priv->stats.Octets += pktlen;
286*0a66de84SNuno Antunes 	priv->stats.Packets++;
287*0a66de84SNuno Antunes 
288*0a66de84SNuno Antunes 	/* Check whether we configured to fix MSS. */
289*0a66de84SNuno Antunes 	if (priv->stats.maxMSS == 0)
290*0a66de84SNuno Antunes 		goto send;
291*0a66de84SNuno Antunes 
292*0a66de84SNuno Antunes #define	M_CHECK(length) do {					\
293*0a66de84SNuno Antunes 	pullup_len += length;					\
294*0a66de84SNuno Antunes 	if ((m)->m_pkthdr.len < pullup_len)			\
295*0a66de84SNuno Antunes 		goto send;					\
296*0a66de84SNuno Antunes 	if ((m)->m_len < pullup_len &&				\
297*0a66de84SNuno Antunes 	   (((m) = m_pullup((m), pullup_len)) == NULL))		\
298*0a66de84SNuno Antunes 		ERROUT(ENOBUFS);				\
299*0a66de84SNuno Antunes 	} while (0)
300*0a66de84SNuno Antunes 
301*0a66de84SNuno Antunes 	/* Check mbuf packet size and arrange for IP header. */
302*0a66de84SNuno Antunes 	M_CHECK(sizeof(struct ip));
303*0a66de84SNuno Antunes 	ip = mtod(m, struct ip *);
304*0a66de84SNuno Antunes 
305*0a66de84SNuno Antunes 	/* Check IP version. */
306*0a66de84SNuno Antunes 	if (ip->ip_v != IPVERSION)
307*0a66de84SNuno Antunes 		ERROUT(EINVAL);
308*0a66de84SNuno Antunes 
309*0a66de84SNuno Antunes 	/* Check IP header length. */
310*0a66de84SNuno Antunes 	iphlen = ip->ip_hl << 2;
311*0a66de84SNuno Antunes 	if (iphlen < sizeof(struct ip) || iphlen > pktlen )
312*0a66de84SNuno Antunes 		ERROUT(EINVAL);
313*0a66de84SNuno Antunes 
314*0a66de84SNuno Antunes         /* Check if it is TCP. */
315*0a66de84SNuno Antunes 	if (!(ip->ip_p == IPPROTO_TCP))
316*0a66de84SNuno Antunes 		goto send;
317*0a66de84SNuno Antunes 
318*0a66de84SNuno Antunes 	/* Check mbuf packet size and arrange for IP+TCP header */
319*0a66de84SNuno Antunes 	M_CHECK(iphlen - sizeof(struct ip) + sizeof(struct tcphdr));
320*0a66de84SNuno Antunes 	ip = mtod(m, struct ip *);
321*0a66de84SNuno Antunes 	tcp = (struct tcphdr *)((caddr_t )ip + iphlen);
322*0a66de84SNuno Antunes 
323*0a66de84SNuno Antunes 	/* Check TCP header length. */
324*0a66de84SNuno Antunes 	tcphlen = tcp->th_off << 2;
325*0a66de84SNuno Antunes 	if (tcphlen < sizeof(struct tcphdr) || tcphlen > pktlen - iphlen)
326*0a66de84SNuno Antunes 		ERROUT(EINVAL);
327*0a66de84SNuno Antunes 
328*0a66de84SNuno Antunes 	/* Check SYN packet and has options. */
329*0a66de84SNuno Antunes 	if (!(tcp->th_flags & TH_SYN) || tcphlen == sizeof(struct tcphdr))
330*0a66de84SNuno Antunes 		goto send;
331*0a66de84SNuno Antunes 
332*0a66de84SNuno Antunes 	/* Update SYN stats. */
333*0a66de84SNuno Antunes 	priv->stats.SYNPkts++;
334*0a66de84SNuno Antunes 
335*0a66de84SNuno Antunes 	M_CHECK(tcphlen - sizeof(struct tcphdr));
336*0a66de84SNuno Antunes 	ip = mtod(m, struct ip *);
337*0a66de84SNuno Antunes 	tcp = (struct tcphdr *)((caddr_t )ip + iphlen);
338*0a66de84SNuno Antunes 
339*0a66de84SNuno Antunes #undef	M_CHECK
340*0a66de84SNuno Antunes 
341*0a66de84SNuno Antunes 	/* Fix MSS and update stats. */
342*0a66de84SNuno Antunes 	if (correct_mss(tcp, tcphlen, priv->stats.maxMSS,
343*0a66de84SNuno Antunes 	    m->m_pkthdr.csum_flags))
344*0a66de84SNuno Antunes 		priv->stats.FixedPkts++;
345*0a66de84SNuno Antunes 
346*0a66de84SNuno Antunes send:
347*0a66de84SNuno Antunes 	/* Deliver frame out destination hook. */
348*0a66de84SNuno Antunes 	NG_FWD_NEW_DATA(error, item, priv->outHook, m);
349*0a66de84SNuno Antunes 
350*0a66de84SNuno Antunes 	return (error);
351*0a66de84SNuno Antunes 
352*0a66de84SNuno Antunes done:
353*0a66de84SNuno Antunes 	NG_FREE_ITEM(item);
354*0a66de84SNuno Antunes 	NG_FREE_M(m);
355*0a66de84SNuno Antunes 
356*0a66de84SNuno Antunes 	return (error);
357*0a66de84SNuno Antunes }
358*0a66de84SNuno Antunes 
359*0a66de84SNuno Antunes /*
360*0a66de84SNuno Antunes  * Hook disconnection.
361*0a66de84SNuno Antunes  * We must check all hooks, since they may reference this one.
362*0a66de84SNuno Antunes  */
363*0a66de84SNuno Antunes static int
ng_tcpmss_disconnect(hook_p hook)364*0a66de84SNuno Antunes ng_tcpmss_disconnect(hook_p hook)
365*0a66de84SNuno Antunes {
366*0a66de84SNuno Antunes 	node_p node = NG_HOOK_NODE(hook);
367*0a66de84SNuno Antunes 	hook_p hook2;
368*0a66de84SNuno Antunes 
369*0a66de84SNuno Antunes 	LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) {
370*0a66de84SNuno Antunes 		hpriv_p priv = NG_HOOK_PRIVATE(hook2);
371*0a66de84SNuno Antunes 
372*0a66de84SNuno Antunes 		if (priv->outHook == hook)
373*0a66de84SNuno Antunes 			priv->outHook = NULL;
374*0a66de84SNuno Antunes 	}
375*0a66de84SNuno Antunes 
376*0a66de84SNuno Antunes 	kfree(NG_HOOK_PRIVATE(hook), M_NETGRAPH);
377*0a66de84SNuno Antunes 
378*0a66de84SNuno Antunes 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
379*0a66de84SNuno Antunes 		ng_rmnode_self(NG_HOOK_NODE(hook));
380*0a66de84SNuno Antunes 
381*0a66de84SNuno Antunes 	return (0);
382*0a66de84SNuno Antunes }
383*0a66de84SNuno Antunes 
384*0a66de84SNuno Antunes /*
385*0a66de84SNuno Antunes  * Code from tcpmssd.
386*0a66de84SNuno Antunes  */
387*0a66de84SNuno Antunes 
388*0a66de84SNuno Antunes /*-
389*0a66de84SNuno Antunes  * The following macro is used to update an
390*0a66de84SNuno Antunes  * internet checksum.  "acc" is a 32-bit
391*0a66de84SNuno Antunes  * accumulation of all the changes to the
392*0a66de84SNuno Antunes  * checksum (adding in old 16-bit words and
393*0a66de84SNuno Antunes  * subtracting out new words), and "cksum"
394*0a66de84SNuno Antunes  * is the checksum value to be updated.
395*0a66de84SNuno Antunes  */
396*0a66de84SNuno Antunes #define TCPMSS_ADJUST_CHECKSUM(acc, cksum) do {		\
397*0a66de84SNuno Antunes 	acc += cksum;					\
398*0a66de84SNuno Antunes 	if (acc < 0) {					\
399*0a66de84SNuno Antunes 		acc = -acc;				\
400*0a66de84SNuno Antunes 		acc = (acc >> 16) + (acc & 0xffff);	\
401*0a66de84SNuno Antunes 		acc += acc >> 16;			\
402*0a66de84SNuno Antunes 		cksum = (u_short) ~acc;			\
403*0a66de84SNuno Antunes 	} else {					\
404*0a66de84SNuno Antunes 		acc = (acc >> 16) + (acc & 0xffff);	\
405*0a66de84SNuno Antunes 		acc += acc >> 16;			\
406*0a66de84SNuno Antunes 		cksum = (u_short) acc;			\
407*0a66de84SNuno Antunes 	}						\
408*0a66de84SNuno Antunes } while (0);
409*0a66de84SNuno Antunes 
410*0a66de84SNuno Antunes static int
correct_mss(struct tcphdr * tc,int hlen,uint16_t maxmss,int flags)411*0a66de84SNuno Antunes correct_mss(struct tcphdr *tc, int hlen, uint16_t maxmss, int flags)
412*0a66de84SNuno Antunes {
413*0a66de84SNuno Antunes 	int olen, optlen;
414*0a66de84SNuno Antunes 	u_char *opt;
415*0a66de84SNuno Antunes 	uint16_t *mss;
416*0a66de84SNuno Antunes 	int accumulate;
417*0a66de84SNuno Antunes 	int res = 0;
418*0a66de84SNuno Antunes 
419*0a66de84SNuno Antunes 	for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1);
420*0a66de84SNuno Antunes 	     olen > 0; olen -= optlen, opt += optlen) {
421*0a66de84SNuno Antunes 		if (*opt == TCPOPT_EOL)
422*0a66de84SNuno Antunes 			break;
423*0a66de84SNuno Antunes 		else if (*opt == TCPOPT_NOP)
424*0a66de84SNuno Antunes 			optlen = 1;
425*0a66de84SNuno Antunes 		else {
426*0a66de84SNuno Antunes 			optlen = *(opt + 1);
427*0a66de84SNuno Antunes 			if (optlen <= 0 || optlen > olen)
428*0a66de84SNuno Antunes 				break;
429*0a66de84SNuno Antunes 			if (*opt == TCPOPT_MAXSEG) {
430*0a66de84SNuno Antunes 				if (optlen != TCPOLEN_MAXSEG)
431*0a66de84SNuno Antunes 					continue;
432*0a66de84SNuno Antunes 				mss = (uint16_t *)(opt + 2);
433*0a66de84SNuno Antunes 				if (ntohs(*mss) > maxmss) {
434*0a66de84SNuno Antunes 					accumulate = *mss;
435*0a66de84SNuno Antunes 					*mss = htons(maxmss);
436*0a66de84SNuno Antunes 					accumulate -= *mss;
437*0a66de84SNuno Antunes 					if ((flags & CSUM_TCP) == 0)
438*0a66de84SNuno Antunes 						TCPMSS_ADJUST_CHECKSUM(accumulate, tc->th_sum);
439*0a66de84SNuno Antunes 					res = 1;
440*0a66de84SNuno Antunes 				}
441*0a66de84SNuno Antunes 			}
442*0a66de84SNuno Antunes 		}
443*0a66de84SNuno Antunes 	}
444*0a66de84SNuno Antunes 	return (res);
445*0a66de84SNuno Antunes }
446