Print this page
5045 use atomic_{inc,dec}_* instead of atomic_add_*
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/inet/ilb/ilb_nat.c
+++ new/usr/src/uts/common/inet/ilb/ilb_nat.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 #include <sys/types.h>
28 28 #include <sys/cmn_err.h>
29 29 #include <sys/crc32.h>
30 30 #include <netinet/in.h>
31 31 #include <inet/ip.h>
32 32 #include <inet/ip6.h>
33 33 #include <inet/tcp.h>
34 34 #include <inet/udp_impl.h>
35 35 #include <inet/ilb.h>
36 36
37 37 #include "ilb_impl.h"
38 38 #include "ilb_stack.h"
39 39 #include "ilb_nat.h"
40 40
41 41 /*
42 42 * NAT source entry garbarge collection timeout. The actual timeout value
43 43 * includes a random jitter bounded by the ILB_NAT_SRC_TIMEOUT_JITTER.
44 44 */
45 45 #define ILB_NAT_SRC_TIMEOUT 30
46 46 #define ILB_NAT_SRC_TIMEOUT_JITTER 5
47 47
48 48 /* key1/2 are assumed to be uint32_t. */
49 49 #define ILB_NAT_SRC_HASH(hash, key1, key2, hash_size) \
50 50 { \
51 51 CRC32((hash), (key1), sizeof (uint32_t), -1U, crc32_table); \
52 52 CRC32((hash), (key2), sizeof (uint32_t), (hash), crc32_table); \
53 53 (hash) %= (hash_size); \
54 54 }
55 55
56 56 /* NAT source port space instance number. */
57 57 static uint32_t ilb_nat_src_instance = 0;
58 58
59 59 static void
60 60 incr_addr(in6_addr_t *a)
61 61 {
62 62 uint32_t i;
63 63
64 64 i = ntohl(a->s6_addr32[3]);
65 65 if (IN6_IS_ADDR_V4MAPPED(a)) {
66 66 a->s6_addr32[3] = htonl(++i);
67 67 ASSERT(i != 0);
68 68 return;
69 69 }
70 70
71 71 if (++i != 0) {
72 72 a->s6_addr32[3] = htonl(i);
73 73 return;
74 74 }
75 75 a->s6_addr32[3] = 0;
76 76 i = ntohl(a->s6_addr[2]);
77 77 if (++i != 0) {
78 78 a->s6_addr32[2] = htonl(i);
79 79 return;
80 80 }
81 81 a->s6_addr32[2] = 0;
82 82 i = ntohl(a->s6_addr[1]);
83 83 if (++i != 0) {
84 84 a->s6_addr32[1] = htonl(i);
85 85 return;
86 86 }
87 87 a->s6_addr32[1] = 0;
88 88 i = ntohl(a->s6_addr[0]);
89 89 a->s6_addr[0] = htonl(++i);
90 90 ASSERT(i != 0);
91 91 }
92 92
93 93 /*
94 94 * When ILB does full NAT, it first picks one source address from the rule's
95 95 * specified NAT source address list (currently done in round robin fashion).
96 96 * Then it needs to allocate a port. This source port must make the tuple
97 97 * (source address:source port:destination address:destination port)
98 98 * unique. The destination part of the tuple is determined by the back
99 99 * end server, and could not be changed.
100 100 *
101 101 * To handle the above source port number allocation, ILB sets up a table
102 102 * of entries identified by source address:back end server address:server port
103 103 * tuple. This table is used by all rules for NAT source port allocation.
104 104 * Each tuple has an associated vmem arena used for managing the NAT source
105 105 * port space between the source address and back end server address/port.
106 106 * Each back end server (ilb_server_t) has an array of pointers (iser_nat_src)
107 107 * to the different entries in this table for NAT source port allocation.
108 108 * When ILB needs to allocate a NAT source address and port to talk to a back
109 109 * end server, it picks a source address and uses the array pointer to get
110 110 * to an entry. Then it calls vmem_alloc() on the associated vmem arena to
111 111 * find an unused port.
112 112 *
113 113 * When a back end server is added, ILB sets up the aforementioned array.
114 114 * For each source address specified in the rule, ILB checks if there is any
115 115 * existing entry which matches this source address:back end server address:
116 116 * port tuple. The server port is either a specific port or 0 (meaning wild
117 117 * card port). Normally, a back end server uses the same port as in the rule.
118 118 * If a back end server is used to serve two different rules, there will be
119 119 * two different ports. Source port allocation for these two rules do not
120 120 * conflict, hence we can use two vmem arenas (two different entries in the
121 121 * table). But if a server uses port range in one rule, we will treat it as
122 122 * a wild card port. Wild card poart matches with any port. If this server
123 123 * is used to serve more than one rules and those rules use the same set of
124 124 * NAT source addresses, this means that they must share the same set of vmem
125 125 * arenas (source port spaces). We do this for simplicity reason. If not,
126 126 * we need to partition the port range so that we can identify different forms
127 127 * of source port number collision.
128 128 */
129 129
130 130 /*
131 131 * NAT source address initialization routine.
132 132 */
133 133 void
134 134 ilb_nat_src_init(ilb_stack_t *ilbs)
135 135 {
136 136 int i;
137 137
138 138 ilbs->ilbs_nat_src = kmem_zalloc(sizeof (ilb_nat_src_hash_t) *
139 139 ilbs->ilbs_nat_src_hash_size, KM_SLEEP);
140 140 for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
141 141 list_create(&ilbs->ilbs_nat_src[i].nsh_head,
142 142 sizeof (ilb_nat_src_entry_t),
143 143 offsetof(ilb_nat_src_entry_t, nse_link));
144 144 mutex_init(&ilbs->ilbs_nat_src[i].nsh_lock, NULL,
145 145 MUTEX_DEFAULT, NULL);
146 146 }
147 147 ilbs->ilbs_nat_src_tid = timeout(ilb_nat_src_timer, ilbs,
148 148 SEC_TO_TICK(ILB_NAT_SRC_TIMEOUT +
149 149 gethrtime() % ILB_NAT_SRC_TIMEOUT_JITTER));
150 150 }
151 151
152 152 /*
153 153 * NAT source address clean up routine.
154 154 */
155 155 void
156 156 ilb_nat_src_fini(ilb_stack_t *ilbs)
157 157 {
158 158 ilb_nat_src_entry_t *cur;
159 159 timeout_id_t tid;
160 160 int i;
161 161
162 162 /*
163 163 * By setting ilbs_nat_src_tid to 0, the timer handler will not
164 164 * restart the timer.
165 165 */
166 166 mutex_enter(&ilbs->ilbs_nat_src_lock);
167 167 tid = ilbs->ilbs_nat_src_tid;
168 168 ilbs->ilbs_nat_src_tid = 0;
169 169 mutex_exit(&ilbs->ilbs_nat_src_lock);
170 170 if (tid != 0)
171 171 (void) untimeout(tid);
172 172
173 173 mutex_destroy(&ilbs->ilbs_nat_src_lock);
174 174
175 175 for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
176 176 while ((cur = list_remove_head(&ilbs->ilbs_nat_src[i].nsh_head))
177 177 != NULL) {
178 178 vmem_destroy(cur->nse_port_arena);
179 179 kmem_free(cur, sizeof (ilb_nat_src_entry_t));
180 180 }
181 181 mutex_destroy(&ilbs->ilbs_nat_src[i].nsh_lock);
182 182 }
183 183
184 184 kmem_free(ilbs->ilbs_nat_src, sizeof (ilb_nat_src_hash_t) *
185 185 ilbs->ilbs_nat_src_hash_size);
186 186 ilbs->ilbs_nat_src = NULL;
187 187 }
188 188
189 189 /* An arena name is "ilb_ns" + "_xxxxxxxxxx" */
190 190 #define ARENA_NAMESZ 18
191 191 #define NAT_PORT_START 4096
192 192 #define NAT_PORT_SIZE 65535 - NAT_PORT_START
193 193
194 194 /*
195 195 * Check if the NAT source and back end server pair ilb_nat_src_entry_t
196 196 * exists. If it does, increment the refcnt and return it. If not, create
197 197 * one and return it.
198 198 */
199 199 static ilb_nat_src_entry_t *
200 200 ilb_find_nat_src(ilb_stack_t *ilbs, const in6_addr_t *nat_src,
201 201 const in6_addr_t *serv_addr, in_port_t port)
202 202 {
203 203 ilb_nat_src_entry_t *tmp;
204 204 uint32_t idx;
205 205 char arena_name[ARENA_NAMESZ];
206 206 list_t *head;
207 207
208 208 ILB_NAT_SRC_HASH(idx, &nat_src->s6_addr32[3], &serv_addr->s6_addr32[3],
209 209 ilbs->ilbs_nat_src_hash_size);
210 210 mutex_enter(&ilbs->ilbs_nat_src[idx].nsh_lock);
211 211 head = &ilbs->ilbs_nat_src[idx].nsh_head;
212 212 for (tmp = list_head(head); tmp != NULL; tmp = list_next(head, tmp)) {
213 213 if (IN6_ARE_ADDR_EQUAL(&tmp->nse_src_addr, nat_src) &&
214 214 IN6_ARE_ADDR_EQUAL(&tmp->nse_serv_addr, serv_addr) &&
215 215 (port == tmp->nse_port || port == 0 ||
216 216 tmp->nse_port == 0)) {
217 217 break;
218 218 }
219 219 }
220 220 /* Found one, return it. */
221 221 if (tmp != NULL) {
222 222 tmp->nse_refcnt++;
223 223 mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
224 224 return (tmp);
225 225 }
226 226
227 227 tmp = kmem_alloc(sizeof (ilb_nat_src_entry_t), KM_NOSLEEP);
228 228 if (tmp == NULL) {
↓ open down ↓ |
228 lines elided |
↑ open up ↑ |
229 229 mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
230 230 return (NULL);
231 231 }
232 232 tmp->nse_src_addr = *nat_src;
233 233 tmp->nse_serv_addr = *serv_addr;
234 234 tmp->nse_port = port;
235 235 tmp->nse_nsh_lock = &ilbs->ilbs_nat_src[idx].nsh_lock;
236 236 tmp->nse_refcnt = 1;
237 237
238 238 (void) snprintf(arena_name, ARENA_NAMESZ, "ilb_ns_%u",
239 - atomic_add_32_nv(&ilb_nat_src_instance, 1));
239 + atomic_inc_32_nv(&ilb_nat_src_instance));
240 240 if ((tmp->nse_port_arena = vmem_create(arena_name,
241 241 (void *)NAT_PORT_START, NAT_PORT_SIZE, 1, NULL, NULL, NULL, 1,
242 242 VM_SLEEP | VMC_IDENTIFIER)) == NULL) {
243 243 kmem_free(tmp, sizeof (*tmp));
244 244 return (NULL);
245 245 }
246 246
247 247 list_insert_tail(head, tmp);
248 248 mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
249 249
250 250 return (tmp);
251 251 }
252 252
253 253 /*
254 254 * Create ilb_nat_src_t struct for a ilb_server_t struct.
255 255 */
256 256 int
257 257 ilb_create_nat_src(ilb_stack_t *ilbs, ilb_nat_src_t **nat_src,
258 258 const in6_addr_t *srv_addr, in_port_t port, const in6_addr_t *start,
259 259 int num)
260 260 {
261 261 ilb_nat_src_t *src;
262 262 in6_addr_t cur_addr;
263 263 int i;
264 264
265 265 if ((src = kmem_zalloc(sizeof (ilb_nat_src_t), KM_NOSLEEP)) == NULL) {
266 266 *nat_src = NULL;
267 267 return (ENOMEM);
268 268 }
269 269 cur_addr = *start;
270 270 for (i = 0; i < num && i < ILB_MAX_NAT_SRC; i++) {
271 271 src->src_list[i] = ilb_find_nat_src(ilbs, &cur_addr, srv_addr,
272 272 port);
273 273 if (src->src_list[i] == NULL) {
274 274 ilb_destroy_nat_src(&src);
275 275 *nat_src = NULL;
276 276 return (ENOMEM);
277 277 }
278 278 incr_addr(&cur_addr);
279 279 /*
280 280 * Increment num_src here so that we can call
281 281 * ilb_destroy_nat_src() when we need to do cleanup.
282 282 */
283 283 src->num_src++;
284 284 }
285 285 *nat_src = src;
286 286 return (0);
287 287 }
288 288
289 289 /*
290 290 * Timer routine for garbage collecting unneeded NAT source entry. We
291 291 * don't use a taskq for this since the table should be relatively small
292 292 * and should be OK for a timer to handle.
293 293 */
294 294 void
295 295 ilb_nat_src_timer(void *arg)
296 296 {
297 297 ilb_stack_t *ilbs = (ilb_stack_t *)arg;
298 298 ilb_nat_src_entry_t *cur, *tmp;
299 299 list_t *head;
300 300 int i;
301 301
302 302 for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
303 303 mutex_enter(&ilbs->ilbs_nat_src[i].nsh_lock);
304 304 head = &ilbs->ilbs_nat_src[i].nsh_head;
305 305 cur = list_head(head);
306 306 while (cur != NULL) {
307 307 /*
308 308 * When a server is removed, it will release its
309 309 * reference on an entry. But there may still be
310 310 * conn using some ports. So check the size also.
311 311 */
312 312 if (cur->nse_refcnt != 0 ||
313 313 vmem_size(cur->nse_port_arena, VMEM_ALLOC) != 0) {
314 314 cur = list_next(head, cur);
315 315 continue;
316 316 }
317 317 tmp = cur;
318 318 cur = list_next(head, cur);
319 319 list_remove(head, tmp);
320 320 vmem_destroy(tmp->nse_port_arena);
321 321 kmem_free(tmp, sizeof (ilb_nat_src_entry_t));
322 322 }
323 323 mutex_exit(&ilbs->ilbs_nat_src[i].nsh_lock);
324 324 }
325 325
326 326 mutex_enter(&ilbs->ilbs_nat_src_lock);
327 327 if (ilbs->ilbs_nat_src_tid == 0) {
328 328 mutex_exit(&ilbs->ilbs_nat_src_lock);
329 329 } else {
330 330 ilbs->ilbs_nat_src_tid = timeout(ilb_nat_src_timer, ilbs,
331 331 SEC_TO_TICK(ILB_NAT_SRC_TIMEOUT +
332 332 gethrtime() % ILB_NAT_SRC_TIMEOUT_JITTER));
333 333 mutex_exit(&ilbs->ilbs_nat_src_lock);
334 334 }
335 335 }
336 336
337 337 /*
338 338 * Destroy a given ilb_nat_src_t struct. It will also release the reference
339 339 * hold on all its ilb_nat_src_entry_t.
340 340 */
341 341 void
342 342 ilb_destroy_nat_src(ilb_nat_src_t **nat_src)
343 343 {
344 344 int i, size;
345 345 ilb_nat_src_t *src;
346 346 ilb_nat_src_entry_t *entry;
347 347
348 348 src = *nat_src;
349 349 if (src == NULL)
350 350 return;
351 351 size = src->num_src;
352 352 /*
353 353 * Set each entry to be condemned and the garbarge collector will
354 354 * clean them up.
355 355 */
356 356 for (i = 0; i < size; i++) {
357 357 entry = src->src_list[i];
358 358 mutex_enter(entry->nse_nsh_lock);
359 359 entry->nse_refcnt--;
360 360 mutex_exit(entry->nse_nsh_lock);
361 361 }
362 362 kmem_free(src, sizeof (ilb_nat_src_t));
363 363 *nat_src = NULL;
364 364 }
365 365
366 366 /*
367 367 * Given a backend server address and its ilb_nat_src_t, allocate a source
368 368 * address and port for NAT usage.
369 369 */
370 370 ilb_nat_src_entry_t *
371 371 ilb_alloc_nat_addr(ilb_nat_src_t *src, in6_addr_t *addr, in_port_t *port,
372 372 uint16_t *nat_src_idx)
373 373 {
374 374 int i, try, size;
375 375 in_port_t p;
376 376
377 377 size = src->num_src;
378 378 /* Increment of cur does not need to be atomic. It is just a hint. */
379 379 if (nat_src_idx == NULL)
380 380 i = (++src->cur) % size;
381 381 else
382 382 i = *nat_src_idx;
383 383
384 384 for (try = 0; try < size; try++) {
385 385 p = (in_port_t)(uintptr_t)vmem_alloc(
386 386 src->src_list[i]->nse_port_arena, 1, VM_NOSLEEP);
387 387 if (p != 0)
388 388 break;
389 389 /*
390 390 * If an index is given and we cannot allocate a port using
391 391 * that entry, return NULL.
392 392 */
393 393 if (nat_src_idx != NULL)
394 394 return (NULL);
395 395 i = (i + 1) % size;
396 396 }
397 397 if (try == size)
398 398 return (NULL);
399 399 *addr = src->src_list[i]->nse_src_addr;
400 400 *port = htons(p);
401 401 return (src->src_list[i]);
402 402 }
403 403
404 404 /*
405 405 * Use the pre-calculated checksum to adjust the checksum of a packet after
406 406 * NAT.
407 407 */
408 408 static void
409 409 adj_cksum(uint16_t *chksum, uint32_t adj_sum)
410 410 {
411 411 adj_sum += (uint16_t)~(*chksum);
412 412 while ((adj_sum >> 16) != 0)
413 413 adj_sum = (adj_sum & 0xffff) + (adj_sum >> 16);
414 414 *chksum = (uint16_t)~adj_sum;
415 415 }
416 416
417 417 /* Do full NAT (replace both source and desination info) on a packet. */
418 418 void
419 419 ilb_full_nat(int l3, void *iph, int l4, void *tph, ilb_nat_info_t *info,
420 420 uint32_t adj_ip_sum, uint32_t adj_tp_sum, boolean_t c2s)
421 421 {
422 422 in_port_t *orig_sport, *orig_dport;
423 423 uint16_t *tp_cksum;
424 424
425 425 switch (l4) {
426 426 case IPPROTO_TCP:
427 427 orig_sport = &((tcpha_t *)tph)->tha_lport;
428 428 orig_dport = &((tcpha_t *)tph)->tha_fport;
429 429 tp_cksum = &((tcpha_t *)tph)->tha_sum;
430 430 break;
431 431 case IPPROTO_UDP:
432 432 orig_sport = &((udpha_t *)tph)->uha_src_port;
433 433 orig_dport = &((udpha_t *)tph)->uha_dst_port;
434 434 tp_cksum = &((udpha_t *)tph)->uha_checksum;
435 435 break;
436 436 default:
437 437 ASSERT(0);
438 438 return;
439 439 }
440 440
441 441 switch (l3) {
442 442 case IPPROTO_IP: {
443 443 ipha_t *ipha;
444 444
445 445 ipha = iph;
446 446 if (c2s) {
447 447 IN6_V4MAPPED_TO_IPADDR(&info->nat_src,
448 448 ipha->ipha_src);
449 449 IN6_V4MAPPED_TO_IPADDR(&info->nat_dst,
450 450 ipha->ipha_dst);
451 451 *orig_sport = info->nat_sport;
452 452 *orig_dport = info->nat_dport;
453 453 } else {
454 454 IN6_V4MAPPED_TO_IPADDR(&info->vip, ipha->ipha_src);
455 455 IN6_V4MAPPED_TO_IPADDR(&info->src, ipha->ipha_dst);
456 456 *orig_sport = info->dport;
457 457 *orig_dport = info->sport;
458 458 }
459 459 adj_cksum(&ipha->ipha_hdr_checksum, adj_ip_sum);
460 460 adj_cksum(tp_cksum, adj_tp_sum);
461 461 break;
462 462 }
463 463 case IPPROTO_IPV6: {
464 464 ip6_t *ip6h;
465 465
466 466 ip6h = iph;
467 467 if (c2s) {
468 468 ip6h->ip6_src = info->nat_src;
469 469 ip6h->ip6_dst = info->nat_dst;
470 470 *orig_sport = info->nat_sport;
471 471 *orig_dport = info->nat_dport;
472 472 } else {
473 473 ip6h->ip6_src = info->vip;
474 474 ip6h->ip6_dst = info->src;
475 475 *orig_sport = info->dport;
476 476 *orig_dport = info->sport;
477 477 }
478 478 /* No checksum for IPv6 header */
479 479 adj_cksum(tp_cksum, adj_tp_sum);
480 480 break;
481 481 }
482 482 default:
483 483 ASSERT(0);
484 484 break;
485 485 }
486 486 }
487 487
488 488 /* Do half NAT (only replace the destination info) on a packet. */
489 489 void
490 490 ilb_half_nat(int l3, void *iph, int l4, void *tph, ilb_nat_info_t *info,
491 491 uint32_t adj_ip_sum, uint32_t adj_tp_sum, boolean_t c2s)
492 492 {
493 493 in_port_t *orig_port;
494 494 uint16_t *tp_cksum;
495 495
496 496 switch (l4) {
497 497 case IPPROTO_TCP:
498 498 if (c2s)
499 499 orig_port = &((tcpha_t *)tph)->tha_fport;
500 500 else
501 501 orig_port = &((tcpha_t *)tph)->tha_lport;
502 502 tp_cksum = &((tcpha_t *)tph)->tha_sum;
503 503 break;
504 504 case IPPROTO_UDP:
505 505 if (c2s)
506 506 orig_port = &((udpha_t *)tph)->uha_dst_port;
507 507 else
508 508 orig_port = &((udpha_t *)tph)->uha_src_port;
509 509 tp_cksum = &((udpha_t *)tph)->uha_checksum;
510 510 break;
511 511 default:
512 512 ASSERT(0);
513 513 return;
514 514 }
515 515
516 516 switch (l3) {
517 517 case IPPROTO_IP: {
518 518 ipha_t *ipha;
519 519
520 520 ipha = iph;
521 521 if (c2s) {
522 522 IN6_V4MAPPED_TO_IPADDR(&info->nat_dst,
523 523 ipha->ipha_dst);
524 524 *orig_port = info->nat_dport;
525 525 } else {
526 526 IN6_V4MAPPED_TO_IPADDR(&info->vip, ipha->ipha_src);
527 527 *orig_port = info->dport;
528 528 }
529 529 adj_cksum(&ipha->ipha_hdr_checksum, adj_ip_sum);
530 530 adj_cksum(tp_cksum, adj_tp_sum);
531 531 break;
532 532 }
533 533 case IPPROTO_IPV6: {
534 534 ip6_t *ip6h;
535 535
536 536 ip6h = iph;
537 537 if (c2s) {
538 538 ip6h->ip6_dst = info->nat_dst;
539 539 *orig_port = info->nat_dport;
540 540 } else {
541 541 ip6h->ip6_src = info->vip;
542 542 *orig_port = info->dport;
543 543 }
544 544 /* No checksum for IPv6 header */
545 545 adj_cksum(tp_cksum, adj_tp_sum);
546 546 break;
547 547 }
548 548 default:
549 549 ASSERT(0);
550 550 break;
551 551 }
552 552 }
553 553
554 554 /* Calculate the IPv6 pseudo checksum, used for ICMPv6 NAT. */
555 555 uint32_t
556 556 ilb_pseudo_sum_v6(ip6_t *ip6h, uint8_t nxt_hdr)
557 557 {
558 558 uint32_t sum;
559 559 uint16_t *cur;
560 560
561 561 cur = (uint16_t *)&ip6h->ip6_src;
562 562 sum = cur[0] + cur[1] + cur[2] + cur[3] + cur[4] + cur[5] + cur[6] +
563 563 cur[7] + cur[8] + cur[9] + cur[10] + cur[11] + cur[12] + cur[13] +
564 564 cur[14] + cur[15] + htons(nxt_hdr);
565 565 return ((sum & 0xffff) + (sum >> 16));
566 566 }
567 567
568 568 /* Do NAT on an ICMPv4 packet. */
569 569 void
570 570 ilb_nat_icmpv4(mblk_t *mp, ipha_t *out_iph, icmph_t *icmph, ipha_t *in_iph,
571 571 in_port_t *sport, in_port_t *dport, ilb_nat_info_t *info, uint32_t sum,
572 572 boolean_t full_nat)
573 573 {
574 574 if (full_nat) {
575 575 IN6_V4MAPPED_TO_IPADDR(&info->nat_src, out_iph->ipha_src);
576 576 IN6_V4MAPPED_TO_IPADDR(&info->nat_src, in_iph->ipha_dst);
577 577 *dport = info->nat_sport;
578 578 }
579 579 IN6_V4MAPPED_TO_IPADDR(&info->nat_dst, out_iph->ipha_dst);
580 580 adj_cksum(&out_iph->ipha_hdr_checksum, sum);
581 581 IN6_V4MAPPED_TO_IPADDR(&info->nat_dst, in_iph->ipha_src);
582 582 *sport = info->nat_dport;
583 583
584 584 icmph->icmph_checksum = 0;
585 585 icmph->icmph_checksum = IP_CSUM(mp, IPH_HDR_LENGTH(out_iph), 0);
586 586 }
587 587
588 588 /* Do NAT on an ICMPv6 packet. */
589 589 void
590 590 ilb_nat_icmpv6(mblk_t *mp, ip6_t *out_ip6h, icmp6_t *icmp6h, ip6_t *in_ip6h,
591 591 in_port_t *sport, in_port_t *dport, ilb_nat_info_t *info,
592 592 boolean_t full_nat)
593 593 {
594 594 int hdr_len;
595 595
596 596 if (full_nat) {
597 597 out_ip6h->ip6_src = info->nat_src;
598 598 in_ip6h->ip6_dst = info->nat_src;
599 599 *dport = info->nat_sport;
600 600 }
601 601 out_ip6h->ip6_dst = info->nat_dst;
602 602 in_ip6h->ip6_src = info->nat_dst;
603 603 *sport = info->nat_dport;
604 604
605 605 icmp6h->icmp6_cksum = out_ip6h->ip6_plen;
606 606 hdr_len = (char *)icmp6h - (char *)out_ip6h;
607 607 icmp6h->icmp6_cksum = IP_CSUM(mp, hdr_len,
608 608 ilb_pseudo_sum_v6(out_ip6h, IPPROTO_ICMPV6));
609 609 }
↓ open down ↓ |
360 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX