xref: /netbsd-src/sys/net/npf/npf_inet.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: npf_inet.c,v 1.1 2010/08/22 18:56:22 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This material is based upon work partially supported by The
8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Various procotol related helper routines.
34  */
35 
36 #ifdef _KERNEL
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.1 2010/08/22 18:56:22 rmind Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 
43 #include <netinet/in_systm.h>
44 #include <netinet/in.h>
45 #include <netinet/ip.h>
46 #include <netinet/tcp.h>
47 #include <netinet/udp.h>
48 #include <netinet/ip_icmp.h>
49 
50 #include <net/if.h>
51 #include <net/ethertypes.h>
52 #include <net/if_ether.h>
53 #endif
54 #include <net/pfil.h>
55 
56 #include "npf_impl.h"
57 
58 /*
59  * npf_fixup{16,32}_cksum: update IPv4 checksum.
60  */
61 
62 uint16_t
63 npf_fixup16_cksum(uint16_t cksum, uint16_t odatum, uint16_t ndatum)
64 {
65 	uint32_t sum;
66 
67 	/*
68 	 * RFC 1624:
69 	 *	HC' = ~(~HC + ~m + m')
70 	 */
71 	sum = ~ntohs(cksum) & 0xffff;
72 	sum += (~ntohs(odatum) & 0xffff) + ntohs(ndatum);
73 	sum = (sum >> 16) + (sum & 0xffff);
74 	sum += (sum >> 16);
75 
76 	return htons(~sum & 0xffff);
77 }
78 
79 uint16_t
80 npf_fixup32_cksum(uint16_t cksum, uint32_t odatum, uint32_t ndatum)
81 {
82 
83 	cksum = npf_fixup16_cksum(cksum, odatum & 0xffff, ndatum & 0xffff);
84 	cksum = npf_fixup16_cksum(cksum, odatum >> 16, ndatum >> 16);
85 	return cksum;
86 }
87 
88 /*
89  * npf_ip4_proto: check IPv4 header length and match protocol number.
90  *
91  * => Returns pointer to protocol header or NULL on failure.
92  * => Stores protocol number in the cache.
93  * => Updates nbuf pointer to header's nbuf.
94  */
95 bool
96 npf_ip4_proto(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
97 {
98 	u_int hlen, offby;
99 	uint8_t val8;
100 	int error;
101 
102 	/* IPv4 header: check IP version and header length. */
103 	error = nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &val8);
104 	if (error || (val8 >> 4) != IPVERSION)
105 		return false;
106 	hlen = (val8 & 0xf) << 2;
107 	if (hlen < sizeof(struct ip))
108 		return false;
109 	offby = offsetof(struct ip, ip_off);
110 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
111 		return false;
112 
113 	/* IPv4 header: check fragment offset. */
114 	error = nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &val8);
115 	if (error || (val8 & ~htons(IP_DF | IP_RF)))
116 		return false;
117 
118 	/* Get and match protocol. */
119 	KASSERT(offsetof(struct ip, ip_p) > offby);
120 	offby = offsetof(struct ip, ip_p) - offby;
121 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
122 		return false;
123 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &val8))
124 		return false;
125 
126 	/* IP checksum. */
127 	offby = offsetof(struct ip, ip_sum) - offsetof(struct ip, ip_p);
128 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
129 		return false;
130 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint16_t), &npc->npc_ipsum))
131 		return false;
132 
133 	/* Cache: IPv4, protocol, header length. */
134 	npc->npc_info |= NPC_IP46;
135 	npc->npc_proto = val8;
136 	npc->npc_hlen = hlen;
137 	return true;
138 }
139 
140 /*
141  * npf_fetch_ip4addrs: fetch source and destination address from IPv4 header.
142  *
143  * => Stores both source and destination addresses into the cache.
144  */
145 bool
146 npf_fetch_ip4addrs(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
147 {
148 	u_int offby;
149 
150 	/* Source address. */
151 	offby = offsetof(struct ip, ip_src);
152 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
153 		return false;
154 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(in_addr_t), &npc->npc_srcip))
155 		return false;
156 
157 	/* Destination address. */
158 	offby = offsetof(struct ip, ip_dst) - offby;
159 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
160 		return false;
161 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(in_addr_t), &npc->npc_dstip))
162 		return false;
163 
164 	/* Both addresses are cached. */
165 	npc->npc_info |= NPC_ADDRS;
166 	return true;
167 }
168 
169 /*
170  * npf_fetch_ports: fetch ports from either TCP or UDP header.
171  *
172  * => Stores both source and destination ports into the cache.
173  */
174 bool
175 npf_fetch_ports(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int proto)
176 {
177 	u_int dst_off;
178 
179 	/* Perform checks, advance to TCP/UDP header. */
180 	if (!npf_iscached(npc, NPC_IP46) && !npf_ip4_proto(npc, nbuf, n_ptr))
181 		return false;
182 	n_ptr = nbuf_advance(&nbuf, n_ptr, npc->npc_hlen);
183 	if (n_ptr == NULL || npc->npc_proto != proto)
184 		return false;
185 
186 	/*
187 	 * TCP/UDP header: fetch source and destination ports.  For both
188 	 * protocols offset of the source port offset is 0.
189 	 */
190 	CTASSERT(offsetof(struct tcphdr, th_sport) == 0);
191 	CTASSERT(offsetof(struct udphdr, uh_sport) == 0);
192 	if (proto == IPPROTO_TCP) {
193 		dst_off = offsetof(struct tcphdr, th_dport);
194 	} else {
195 		KASSERT(proto == IPPROTO_UDP);
196 		dst_off = offsetof(struct udphdr, uh_dport);
197 	}
198 
199 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(in_port_t), &npc->npc_sport))
200 		return false;
201 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, dst_off)) == NULL)
202 		return false;
203 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(in_port_t), &npc->npc_dport))
204 		return false;
205 
206 	/* Both ports are cached. */
207 	npc->npc_info |= NPC_PORTS;
208 	return true;
209 }
210 
211 /*
212  * npf_fetch_icmp: fetch ICMP code, type and possible query ID.
213  *
214  * => Stores both all fetched items into the cache.
215  */
216 bool
217 npf_fetch_icmp(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
218 {
219 	u_int offby;
220 	uint8_t type;
221 
222 	KASSERT(npf_iscached(npc, NPC_IP46));
223 
224 	/* ICMP type. */
225 	offby = npc->npc_hlen;
226 	CTASSERT(offsetof(struct icmp, icmp_type) == 0);
227 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
228 		return false;
229 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &type))
230 		return false;
231 
232 	/* ICMP code. */
233 	offby = offsetof(struct icmp, icmp_code);
234 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
235 		return false;
236 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &npc->npc_icmp_code))
237 		return false;
238 
239 	/* Mark as cached. */
240 	npc->npc_icmp_type = type;
241 	npc->npc_info |= NPC_ICMP;
242 	return true;
243 }
244 
245 static inline bool
246 npf_fetch_tcpfl(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
247 {
248 	u_int offby;
249 
250 	/* Get TCP flags. */
251 	offby = npc->npc_hlen + offsetof(struct tcphdr, th_flags);
252 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
253 		return false;
254 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &npc->npc_tcp_flags))
255 		return false;
256 	return true;
257 }
258 
259 /*
260  * npf_cache_all_ip4: general routine to cache all relevant IPv4 and
261  * TCP, UDP or ICMP data.
262  */
263 bool
264 npf_cache_all_ip4(npf_cache_t *npc, nbuf_t *nbuf, const int layer)
265 {
266 	void *n_ptr = nbuf_dataptr(nbuf);
267 	u_int offby;
268 
269 	if (layer == NPF_LAYER_2) {
270 		/* Ethernet: match if ETHERTYPE_IP and if so - advance. */
271 		if (npf_match_ether(nbuf, 1, 0, ETHERTYPE_IP, &offby))
272 			return false;
273 		if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
274 			return false;
275 		/* Cache Ethernet header length. XXX */
276 		npc->npc_elen = offby;
277 	}
278 
279 	/* IPv4: get protocol, source and destination addresses. */
280 	if (!npf_iscached(npc, NPC_IP46) && !npf_ip4_proto(npc, nbuf, n_ptr)) {
281 		return false;
282 	}
283 	if (!npf_iscached(npc, NPC_ADDRS) &&
284 	    !npf_fetch_ip4addrs(npc, nbuf, n_ptr)) {
285 		return false;
286 	}
287 	switch (npc->npc_proto) {
288 	case IPPROTO_TCP:
289 		/* TCP flags. */
290 		if (!npf_fetch_tcpfl(npc, nbuf, n_ptr)) {
291 			return false;
292 		}
293 		/* FALLTHROUGH */
294 
295 	case IPPROTO_UDP:
296 		/* Fetch TCP/UDP ports. */
297 		return npf_fetch_ports(npc, nbuf, n_ptr, npc->npc_proto);
298 
299 	case IPPROTO_ICMP:
300 		/* Fetch ICMP data. */
301 		return npf_fetch_icmp(npc, nbuf, n_ptr);
302 	}
303 	return false;
304 }
305 
306 /*
307  * npf_rwrport: rewrite required TCP/UDP port and update checksum.
308  */
309 bool
310 npf_rwrport(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
311     in_port_t port, in_addr_t naddr)
312 {
313 	const int proto = npc->npc_proto;
314 	u_int offby, toff;
315 	in_addr_t oaddr;
316 	in_port_t oport;
317 	uint16_t cksum;
318 
319 	KASSERT(npf_iscached(npc, NPC_PORTS));
320 	KASSERT(proto == IPPROTO_TCP || proto == IPPROTO_UDP);
321 
322 	offby = npc->npc_hlen;
323 
324 	if (di == PFIL_OUT) {
325 		/* Offset to the source port is zero. */
326 		CTASSERT(offsetof(struct tcphdr, th_sport) == 0);
327 		CTASSERT(offsetof(struct udphdr, uh_sport) == 0);
328 		if (proto == IPPROTO_TCP) {
329 			toff = offsetof(struct tcphdr, th_sum);
330 		} else {
331 			toff = offsetof(struct udphdr, uh_sum);
332 		}
333 		oaddr = npc->npc_srcip;
334 		oport = npc->npc_sport;
335 	} else {
336 		/* Calculate offset to destination port and checksum. */
337 		u_int poff;
338 		if (proto == IPPROTO_TCP) {
339 			poff = offsetof(struct tcphdr, th_dport);
340 			toff = offsetof(struct tcphdr, th_sum) - poff;
341 		} else {
342 			poff = offsetof(struct udphdr, uh_dport);
343 			toff = offsetof(struct udphdr, uh_sum) - poff;
344 		}
345 		oaddr = npc->npc_dstip;
346 		oport = npc->npc_dport;
347 		offby += poff;
348 	}
349 
350 	/* Advance and rewrite port. */
351 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
352 		return false;
353 	if (nbuf_store_datum(nbuf, n_ptr, sizeof(in_port_t), &port))
354 		return false;
355 
356 	/* Advance and update TCP/UDP checksum. */
357 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, toff)) == NULL)
358 		return false;
359 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint16_t), &cksum))
360 		return false;
361 	if (__predict_true(cksum || proto == IPPROTO_TCP)) {
362 		cksum = npf_fixup32_cksum(cksum, oaddr, naddr);
363 		cksum = npf_fixup16_cksum(cksum, oport, port);
364 		if (nbuf_store_datum(nbuf, n_ptr, sizeof(uint16_t), &cksum))
365 			return false;
366 	}
367 	return true;
368 }
369 
370 /*
371  * npf_rwrip: rewrite required IP address and update checksum.
372  */
373 bool
374 npf_rwrip(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
375     in_addr_t addr)
376 {
377 	u_int offby;
378 	in_addr_t oaddr;
379 
380 	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_ADDRS));
381 
382 	/* Advance to the checksum in IP header and fetch it. */
383 	offby = offsetof(struct ip, ip_sum);
384 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
385 		return false;
386 
387 	if (di == PFIL_OUT) {
388 		/* Rewrite source address, if outgoing. */
389 		offby = offsetof(struct ip, ip_src) - offby;
390 		oaddr = npc->npc_srcip;
391 	} else {
392 		/* Rewrite destination, if incoming. */
393 		offby = offsetof(struct ip, ip_dst) - offby;
394 		oaddr = npc->npc_dstip;
395 	}
396 
397 	/* Write new IP checksum (it is acceptable to do this earlier). */
398 	uint16_t cksum = npf_fixup32_cksum(npc->npc_ipsum, oaddr, addr);
399 	if (nbuf_store_datum(nbuf, n_ptr, sizeof(uint16_t), &cksum))
400 		return false;
401 
402 	/* Advance to address and rewrite it. */
403 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL)
404 		return false;
405 	if (nbuf_store_datum(nbuf, n_ptr, sizeof(in_addr_t), &addr))
406 		return false;
407 
408 	npc->npc_ipsum = cksum;
409 	return true;
410 }
411