npf_inet.c revision 1.1 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