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