1 /*- 2 * Copyright (c) 2014-2019 Mindaugas Rasiukevicius <rmind at netbsd org> 3 * Copyright (c) 2010-2014 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This material is based upon work partially supported by The 7 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Connection key -- is a structure encoding the 5-tuple (protocol, address 33 * length, source/destination address and source/destination port/ID). 34 * 35 * Key layout 36 * 37 * The single key is formed out of 32-bit integers. The layout is: 38 * 39 * Field: | proto | alen | src-id | dst-id | src-addr | dst-addr | 40 * +--------+--------+--------+--------+----------+----------+ 41 * Bits: | 16 | 16 | 16 | 16 | 32-128 | 32-128 | 42 * 43 * The source and destination are inverted if the key is for the 44 * backwards stream (forw == false). The address length depends on 45 * the 'alen' field. The length is in bytes and is either 4 or 16. 46 * 47 * Warning: the keys must be immutable while they are in conndb. 48 * 49 * Embedding in the connection structure (npf_conn_t) 50 * 51 * Two keys are stored in the npf_conn_t::c_keys[] array, which is 52 * variable-length, depending on whether the keys store IPv4 or IPv6 53 * addresses. The length of the first key determines the position 54 * of the second key. 55 */ 56 57 #ifdef _KERNEL 58 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: npf_connkey.c,v 1.1 2019/07/23 00:52:01 rmind Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/types.h> 63 #endif 64 65 #define __NPF_CONN_PRIVATE 66 #include "npf_conn.h" 67 #include "npf_impl.h" 68 69 static inline unsigned 70 connkey_setkey(npf_connkey_t *key, uint16_t proto, const void *ipv, 71 const uint16_t *id, unsigned alen, bool forw) 72 { 73 const npf_addr_t * const *ips = ipv; 74 uint32_t *k = key->ck_key; 75 unsigned isrc, idst; 76 77 if (__predict_true(forw)) { 78 isrc = NPF_SRC, idst = NPF_DST; 79 } else { 80 isrc = NPF_DST, idst = NPF_SRC; 81 } 82 83 /* 84 * See the key layout explanation above. 85 */ 86 87 k[0] = ((uint32_t)proto << 16) | (alen & 0xffff); 88 k[1] = ((uint32_t)id[isrc] << 16) | id[idst]; 89 90 if (__predict_true(alen == sizeof(in_addr_t))) { 91 k[2] = ips[isrc]->word32[0]; 92 k[3] = ips[idst]->word32[0]; 93 return 4 * sizeof(uint32_t); 94 } else { 95 const unsigned nwords = alen >> 2; 96 memcpy(&k[2], ips[isrc], alen); 97 memcpy(&k[2 + nwords], ips[idst], alen); 98 return (2 + (nwords * 2)) * sizeof(uint32_t); 99 } 100 } 101 102 static inline void 103 connkey_getkey(const npf_connkey_t *key, uint16_t *proto, npf_addr_t *ips, 104 uint16_t *id, uint16_t *alen) 105 { 106 const uint32_t *k = key->ck_key; 107 108 /* 109 * See the key layout explanation above. 110 */ 111 112 *proto = k[0] >> 16; 113 *alen = k[0] & 0xffff; 114 id[NPF_SRC] = k[1] >> 16; 115 id[NPF_DST] = k[1] & 0xffff; 116 117 switch (*alen) { 118 case sizeof(struct in6_addr): 119 case sizeof(struct in_addr): 120 memcpy(&ips[NPF_SRC], &k[2], *alen); 121 memcpy(&ips[NPF_DST], &k[2 + ((unsigned)*alen >> 2)], *alen); 122 return; 123 default: 124 KASSERT(0); 125 } 126 } 127 128 /* 129 * npf_conn_adjkey: adjust the connection key by resetting the address/port. 130 */ 131 void 132 npf_conn_adjkey(npf_connkey_t *key, const npf_addr_t *naddr, 133 const uint16_t id, const int di) 134 { 135 uint32_t * const k = key->ck_key; 136 const unsigned alen = k[0] & 0xffff; 137 uint32_t *addr = &k[2 + ((alen >> 2) * di)]; 138 139 KASSERT(alen > 0); 140 memcpy(addr, naddr, alen); 141 142 if (id) { 143 const uint32_t oid = k[1]; 144 const unsigned shift = 16 * !di; 145 const uint32_t mask = 0xffff0000 >> shift; 146 k[1] = ((uint32_t)id << shift) | (oid & mask); 147 } 148 } 149 150 /* 151 * npf_conn_conkey: construct a key for the connection lookup. 152 * 153 * => Returns the key length in bytes or zero on failure. 154 */ 155 unsigned 156 npf_conn_conkey(const npf_cache_t *npc, npf_connkey_t *key, const bool forw) 157 { 158 const unsigned proto = npc->npc_proto; 159 const unsigned alen = npc->npc_alen; 160 const struct tcphdr *th; 161 const struct udphdr *uh; 162 uint16_t id[2] = { 0, 0 }; 163 164 switch (proto) { 165 case IPPROTO_TCP: 166 KASSERT(npf_iscached(npc, NPC_TCP)); 167 th = npc->npc_l4.tcp; 168 id[NPF_SRC] = th->th_sport; 169 id[NPF_DST] = th->th_dport; 170 break; 171 case IPPROTO_UDP: 172 KASSERT(npf_iscached(npc, NPC_UDP)); 173 uh = npc->npc_l4.udp; 174 id[NPF_SRC] = uh->uh_sport; 175 id[NPF_DST] = uh->uh_dport; 176 break; 177 case IPPROTO_ICMP: 178 if (npf_iscached(npc, NPC_ICMP_ID)) { 179 const struct icmp *ic = npc->npc_l4.icmp; 180 id[NPF_SRC] = ic->icmp_id; 181 id[NPF_DST] = ic->icmp_id; 182 break; 183 } 184 return 0; 185 case IPPROTO_ICMPV6: 186 if (npf_iscached(npc, NPC_ICMP_ID)) { 187 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6; 188 id[NPF_SRC] = ic6->icmp6_id; 189 id[NPF_DST] = ic6->icmp6_id; 190 break; 191 } 192 return 0; 193 default: 194 /* Unsupported protocol. */ 195 return 0; 196 } 197 return connkey_setkey(key, proto, npc->npc_ips, id, alen, forw); 198 } 199 200 /* 201 * npf_conn_getforwkey: get the address to the "forwards" key. 202 */ 203 npf_connkey_t * 204 npf_conn_getforwkey(npf_conn_t *conn) 205 { 206 return (void *)&conn->c_keys[0]; 207 } 208 209 /* 210 * npf_conn_getbackkey: get the address to the "backwards" key. 211 * 212 * => It depends on the address length. 213 */ 214 npf_connkey_t * 215 npf_conn_getbackkey(npf_conn_t *conn, unsigned alen) 216 { 217 const unsigned off = 2 + ((alen * 2) >> 2); 218 KASSERT(off == NPF_CONNKEY_V4WORDS || off == NPF_CONNKEY_V6WORDS); 219 return (void *)&conn->c_keys[off]; 220 } 221 222 /* 223 * Connection key exporting/importing. 224 */ 225 226 nvlist_t * 227 npf_connkey_export(const npf_connkey_t *key) 228 { 229 uint16_t ids[2], alen, proto; 230 npf_addr_t ips[2]; 231 nvlist_t *kdict; 232 233 kdict = nvlist_create(0); 234 connkey_getkey(key, &proto, ips, ids, &alen); 235 nvlist_add_number(kdict, "proto", proto); 236 nvlist_add_number(kdict, "sport", ids[NPF_SRC]); 237 nvlist_add_number(kdict, "dport", ids[NPF_DST]); 238 nvlist_add_binary(kdict, "saddr", &ips[NPF_SRC], alen); 239 nvlist_add_binary(kdict, "daddr", &ips[NPF_DST], alen); 240 return kdict; 241 } 242 243 unsigned 244 npf_connkey_import(const nvlist_t *kdict, npf_connkey_t *key) 245 { 246 npf_addr_t const * ips[2]; 247 uint16_t proto, ids[2]; 248 size_t alen1, alen2; 249 250 proto = dnvlist_get_number(kdict, "proto", 0); 251 ids[NPF_SRC] = dnvlist_get_number(kdict, "sport", 0); 252 ids[NPF_DST] = dnvlist_get_number(kdict, "dport", 0); 253 ips[NPF_SRC] = dnvlist_get_binary(kdict, "saddr", &alen1, NULL, 0); 254 ips[NPF_DST] = dnvlist_get_binary(kdict, "daddr", &alen2, NULL, 0); 255 if (alen1 == 0 || alen1 > sizeof(npf_addr_t) || alen1 != alen2) { 256 return 0; 257 } 258 return connkey_setkey(key, proto, ips, ids, alen1, true); 259 } 260 261 #if defined(DDB) || defined(_NPF_TESTING) 262 263 void 264 npf_connkey_print(const npf_connkey_t *key) 265 { 266 uint16_t proto, ids[2], alen; 267 npf_addr_t ips[2]; 268 269 connkey_getkey(key, &proto, ips, ids, &alen); 270 printf("\tforw %s:%d", npf_addr_dump(&ips[0], alen), ids[0]); 271 printf("-> %s:%d\n", npf_addr_dump(&ips[1], alen), ids[1]); 272 } 273 274 #endif 275