1 /*- 2 * Copyright (c) 2001 Charles Mott <cmott@scientech.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 /* file: alias_proxy.c 30 31 This file encapsulates special operations related to transparent 32 proxy redirection. This is where packets with a particular destination, 33 usually tcp port 80, are redirected to a proxy server. 34 35 When packets are proxied, the destination address and port are 36 modified. In certain cases, it is necessary to somehow encode 37 the original address/port info into the packet. Two methods are 38 presently supported: addition of a [DEST addr port] string at the 39 beginning a of tcp stream, or inclusion of an optional field 40 in the IP header. 41 42 There is one public API function: 43 44 PacketAliasProxyRule() -- Adds and deletes proxy 45 rules. 46 47 Rules are stored in a linear linked list, so lookup efficiency 48 won't be too good for large lists. 49 50 51 Initial development: April, 1998 (cjm) 52 */ 53 54 55 /* System includes */ 56 #include <ctype.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <netdb.h> 61 62 #include <sys/types.h> 63 #include <sys/socket.h> 64 65 /* BSD IPV4 includes */ 66 #include <netinet/in_systm.h> 67 #include <netinet/in.h> 68 #include <netinet/ip.h> 69 #include <netinet/tcp.h> 70 71 #include <arpa/inet.h> 72 73 #include "alias_local.h" /* Functions used by alias*.c */ 74 #include "alias.h" /* Public API functions for libalias */ 75 76 77 78 /* 79 Data structures 80 */ 81 82 /* 83 * A linked list of arbitrary length, based on struct proxy_entry is 84 * used to store proxy rules. 85 */ 86 struct proxy_entry 87 { 88 #define PROXY_TYPE_ENCODE_NONE 1 89 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 90 #define PROXY_TYPE_ENCODE_IPHDR 3 91 int rule_index; 92 int proxy_type; 93 u_char proto; 94 u_short proxy_port; 95 u_short server_port; 96 97 struct in_addr server_addr; 98 99 struct in_addr src_addr; 100 struct in_addr src_mask; 101 102 struct in_addr dst_addr; 103 struct in_addr dst_mask; 104 105 struct proxy_entry *next; 106 struct proxy_entry *last; 107 }; 108 109 110 111 /* 112 File scope variables 113 */ 114 115 static struct proxy_entry *proxyList; 116 117 118 119 /* Local (static) functions: 120 121 IpMask() -- Utility function for creating IP 122 masks from integer (1-32) specification. 123 IpAddr() -- Utility function for converting string 124 to IP address 125 IpPort() -- Utility function for converting string 126 to port number 127 RuleAdd() -- Adds an element to the rule list. 128 RuleDelete() -- Removes an element from the rule list. 129 RuleNumberDelete() -- Removes all elements from the rule list 130 having a certain rule number. 131 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning 132 of a TCP stream. 133 ProxyEncodeIpHeader() -- Adds an IP option indicating the true 134 destination of a proxied IP packet 135 */ 136 137 static int IpMask(int, struct in_addr *); 138 static int IpAddr(char *, struct in_addr *); 139 static int IpPort(char *, int, int *); 140 static void RuleAdd(struct proxy_entry *); 141 static void RuleDelete(struct proxy_entry *); 142 static int RuleNumberDelete(int); 143 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); 144 static void ProxyEncodeIpHeader(struct ip *, int); 145 146 static int 147 IpMask(int nbits, struct in_addr *mask) 148 { 149 int i; 150 u_int imask; 151 152 if (nbits < 0 || nbits > 32) 153 return -1; 154 155 imask = 0; 156 for (i=0; i<nbits; i++) 157 imask = (imask >> 1) + 0x80000000; 158 mask->s_addr = htonl(imask); 159 160 return 0; 161 } 162 163 static int 164 IpAddr(char *s, struct in_addr *addr) 165 { 166 if (inet_aton(s, addr) == 0) 167 return -1; 168 else 169 return 0; 170 } 171 172 static int 173 IpPort(char *s, int proto, int *port) 174 { 175 int n; 176 177 n = sscanf(s, "%d", port); 178 if (n != 1) 179 { 180 struct servent *se; 181 182 if (proto == IPPROTO_TCP) 183 se = getservbyname(s, "tcp"); 184 else if (proto == IPPROTO_UDP) 185 se = getservbyname(s, "udp"); 186 else 187 return -1; 188 189 if (se == NULL) 190 return -1; 191 192 *port = (u_int) ntohs(se->s_port); 193 } 194 195 return 0; 196 } 197 198 void 199 RuleAdd(struct proxy_entry *entry) 200 { 201 int rule_index; 202 struct proxy_entry *ptr; 203 struct proxy_entry *ptr_last; 204 205 if (proxyList == NULL) 206 { 207 proxyList = entry; 208 entry->last = NULL; 209 entry->next = NULL; 210 return; 211 } 212 213 rule_index = entry->rule_index; 214 ptr = proxyList; 215 ptr_last = NULL; 216 while (ptr != NULL) 217 { 218 if (ptr->rule_index >= rule_index) 219 { 220 if (ptr_last == NULL) 221 { 222 entry->next = proxyList; 223 entry->last = NULL; 224 proxyList->last = entry; 225 proxyList = entry; 226 return; 227 } 228 229 ptr_last->next = entry; 230 ptr->last = entry; 231 entry->last = ptr->last; 232 entry->next = ptr; 233 return; 234 } 235 ptr_last = ptr; 236 ptr = ptr->next; 237 } 238 239 ptr_last->next = entry; 240 entry->last = ptr_last; 241 entry->next = NULL; 242 } 243 244 static void 245 RuleDelete(struct proxy_entry *entry) 246 { 247 if (entry->last != NULL) 248 entry->last->next = entry->next; 249 else 250 proxyList = entry->next; 251 252 if (entry->next != NULL) 253 entry->next->last = entry->last; 254 255 free(entry); 256 } 257 258 static int 259 RuleNumberDelete(int rule_index) 260 { 261 int err; 262 struct proxy_entry *ptr; 263 264 err = -1; 265 ptr = proxyList; 266 while (ptr != NULL) 267 { 268 struct proxy_entry *ptr_next; 269 270 ptr_next = ptr->next; 271 if (ptr->rule_index == rule_index) 272 { 273 err = 0; 274 RuleDelete(ptr); 275 } 276 277 ptr = ptr_next; 278 } 279 280 return err; 281 } 282 283 static void 284 ProxyEncodeTcpStream(struct alias_link *link, 285 struct ip *pip, 286 int maxpacketsize) 287 { 288 int slen; 289 char buffer[40]; 290 struct tcphdr *tc; 291 292 /* Compute pointer to tcp header */ 293 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 294 295 /* Don't modify if once already modified */ 296 297 if (GetAckModified (link)) 298 return; 299 300 /* Translate destination address and port to string form */ 301 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", 302 inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link))); 303 304 /* Pad string out to a multiple of two in length */ 305 slen = strlen(buffer); 306 switch (slen % 2) 307 { 308 case 0: 309 strcat(buffer, " \n"); 310 slen += 2; 311 break; 312 case 1: 313 strcat(buffer, "\n"); 314 slen += 1; 315 } 316 317 /* Check for packet overflow */ 318 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) 319 return; 320 321 /* Shift existing TCP data and insert destination string */ 322 { 323 int dlen; 324 int hlen; 325 u_char *p; 326 327 hlen = (pip->ip_hl + tc->th_off) << 2; 328 dlen = ntohs (pip->ip_len) - hlen; 329 330 /* Modify first packet that has data in it */ 331 332 if (dlen == 0) 333 return; 334 335 p = (char *) pip; 336 p += hlen; 337 338 memmove(p + slen, p, dlen); 339 memcpy(p, buffer, slen); 340 } 341 342 /* Save information about modfied sequence number */ 343 { 344 int delta; 345 346 SetAckModified(link); 347 delta = GetDeltaSeqOut(pip, link); 348 AddSeq(pip, link, delta+slen); 349 } 350 351 /* Update IP header packet length and checksum */ 352 { 353 int accumulate; 354 355 accumulate = pip->ip_len; 356 pip->ip_len = htons(ntohs(pip->ip_len) + slen); 357 accumulate -= pip->ip_len; 358 359 ADJUST_CHECKSUM(accumulate, pip->ip_sum); 360 } 361 362 /* Update TCP checksum, Use TcpChecksum since so many things have 363 already changed. */ 364 365 tc->th_sum = 0; 366 tc->th_sum = TcpChecksum (pip); 367 } 368 369 static void 370 ProxyEncodeIpHeader(struct ip *pip, 371 int maxpacketsize) 372 { 373 #define OPTION_LEN_BYTES 8 374 #define OPTION_LEN_INT16 4 375 #define OPTION_LEN_INT32 2 376 u_char option[OPTION_LEN_BYTES]; 377 378 #ifdef DEBUG 379 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); 380 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); 381 #endif 382 383 /* Check to see that there is room to add an IP option */ 384 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) 385 return; 386 387 /* Build option and copy into packet */ 388 { 389 u_char *ptr; 390 struct tcphdr *tc; 391 392 ptr = (u_char *) pip; 393 ptr += 20; 394 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); 395 396 option[0] = 0x64; /* class: 3 (reserved), option 4 */ 397 option[1] = OPTION_LEN_BYTES; 398 399 memcpy(&option[2], (u_char *) &pip->ip_dst, 4); 400 401 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 402 memcpy(&option[6], (u_char *) &tc->th_sport, 2); 403 404 memcpy(ptr, option, 8); 405 } 406 407 /* Update checksum, header length and packet length */ 408 { 409 int i; 410 int accumulate; 411 u_short *sptr; 412 413 sptr = (u_short *) option; 414 accumulate = 0; 415 for (i=0; i<OPTION_LEN_INT16; i++) 416 accumulate -= *(sptr++); 417 418 sptr = (u_short *) pip; 419 accumulate += *sptr; 420 pip->ip_hl += OPTION_LEN_INT32; 421 accumulate -= *sptr; 422 423 accumulate += pip->ip_len; 424 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); 425 accumulate -= pip->ip_len; 426 427 ADJUST_CHECKSUM(accumulate, pip->ip_sum); 428 } 429 #undef OPTION_LEN_BYTES 430 #undef OPTION_LEN_INT16 431 #undef OPTION_LEN_INT32 432 #ifdef DEBUG 433 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); 434 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); 435 #endif 436 } 437 438 439 /* Functions by other packet alias source files 440 441 ProxyCheck() -- Checks whether an outgoing packet should 442 be proxied. 443 ProxyModify() -- Encodes the original destination address/port 444 for a packet which is to be redirected to 445 a proxy server. 446 */ 447 448 int 449 ProxyCheck(struct ip *pip, 450 struct in_addr *proxy_server_addr, 451 u_short *proxy_server_port) 452 { 453 u_short dst_port; 454 struct in_addr src_addr; 455 struct in_addr dst_addr; 456 struct proxy_entry *ptr; 457 458 src_addr = pip->ip_src; 459 dst_addr = pip->ip_dst; 460 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2))) 461 ->th_dport; 462 463 ptr = proxyList; 464 while (ptr != NULL) 465 { 466 u_short proxy_port; 467 468 proxy_port = ptr->proxy_port; 469 if ((dst_port == proxy_port || proxy_port == 0) 470 && pip->ip_p == ptr->proto 471 && src_addr.s_addr != ptr->server_addr.s_addr) 472 { 473 struct in_addr src_addr_masked; 474 struct in_addr dst_addr_masked; 475 476 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; 477 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; 478 479 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) 480 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) 481 { 482 if ((*proxy_server_port = ptr->server_port) == 0) 483 *proxy_server_port = dst_port; 484 *proxy_server_addr = ptr->server_addr; 485 return ptr->proxy_type; 486 } 487 } 488 ptr = ptr->next; 489 } 490 491 return 0; 492 } 493 494 void 495 ProxyModify(struct alias_link *link, 496 struct ip *pip, 497 int maxpacketsize, 498 int proxy_type) 499 { 500 switch (proxy_type) 501 { 502 case PROXY_TYPE_ENCODE_IPHDR: 503 ProxyEncodeIpHeader(pip, maxpacketsize); 504 break; 505 506 case PROXY_TYPE_ENCODE_TCPSTREAM: 507 ProxyEncodeTcpStream(link, pip, maxpacketsize); 508 break; 509 } 510 } 511 512 513 /* 514 Public API functions 515 */ 516 517 int 518 PacketAliasProxyRule(const char *cmd) 519 { 520 /* 521 * This function takes command strings of the form: 522 * 523 * server <addr>[:<port>] 524 * [port <port>] 525 * [rule n] 526 * [proto tcp|udp] 527 * [src <addr>[/n]] 528 * [dst <addr>[/n]] 529 * [type encode_tcp_stream|encode_ip_hdr|no_encode] 530 * 531 * delete <rule number> 532 * 533 * Subfields can be in arbitrary order. Port numbers and addresses 534 * must be in either numeric or symbolic form. An optional rule number 535 * is used to control the order in which rules are searched. If two 536 * rules have the same number, then search order cannot be guaranteed, 537 * and the rules should be disjoint. If no rule number is specified, 538 * then 0 is used, and group 0 rules are always checked before any 539 * others. 540 */ 541 int i, n, len; 542 int cmd_len; 543 int token_count; 544 int state; 545 char *token; 546 char buffer[256]; 547 char str_port[sizeof(buffer)]; 548 char str_server_port[sizeof(buffer)]; 549 char *res = buffer; 550 551 int rule_index; 552 int proto; 553 int proxy_type; 554 int proxy_port; 555 int server_port; 556 struct in_addr server_addr; 557 struct in_addr src_addr, src_mask; 558 struct in_addr dst_addr, dst_mask; 559 struct proxy_entry *proxy_entry; 560 561 /* Copy command line into a buffer */ 562 cmd += strspn(cmd, " \t"); 563 cmd_len = strlen(cmd); 564 if (cmd_len > (sizeof(buffer) - 1)) 565 return -1; 566 strcpy(buffer, cmd); 567 568 /* Convert to lower case */ 569 len = strlen(buffer); 570 for (i=0; i<len; i++) 571 buffer[i] = tolower((unsigned char)buffer[i]); 572 573 /* Set default proxy type */ 574 575 /* Set up default values */ 576 rule_index = 0; 577 proxy_type = PROXY_TYPE_ENCODE_NONE; 578 proto = IPPROTO_TCP; 579 proxy_port = 0; 580 server_addr.s_addr = 0; 581 server_port = 0; 582 src_addr.s_addr = 0; 583 IpMask(0, &src_mask); 584 dst_addr.s_addr = 0; 585 IpMask(0, &dst_mask); 586 587 str_port[0] = 0; 588 str_server_port[0] = 0; 589 590 /* Parse command string with state machine */ 591 #define STATE_READ_KEYWORD 0 592 #define STATE_READ_TYPE 1 593 #define STATE_READ_PORT 2 594 #define STATE_READ_SERVER 3 595 #define STATE_READ_RULE 4 596 #define STATE_READ_DELETE 5 597 #define STATE_READ_PROTO 6 598 #define STATE_READ_SRC 7 599 #define STATE_READ_DST 8 600 state = STATE_READ_KEYWORD; 601 token = strsep(&res, " \t"); 602 token_count = 0; 603 while (token != NULL) 604 { 605 token_count++; 606 switch (state) 607 { 608 case STATE_READ_KEYWORD: 609 if (strcmp(token, "type") == 0) 610 state = STATE_READ_TYPE; 611 else if (strcmp(token, "port") == 0) 612 state = STATE_READ_PORT; 613 else if (strcmp(token, "server") == 0) 614 state = STATE_READ_SERVER; 615 else if (strcmp(token, "rule") == 0) 616 state = STATE_READ_RULE; 617 else if (strcmp(token, "delete") == 0) 618 state = STATE_READ_DELETE; 619 else if (strcmp(token, "proto") == 0) 620 state = STATE_READ_PROTO; 621 else if (strcmp(token, "src") == 0) 622 state = STATE_READ_SRC; 623 else if (strcmp(token, "dst") == 0) 624 state = STATE_READ_DST; 625 else 626 return -1; 627 break; 628 629 case STATE_READ_TYPE: 630 if (strcmp(token, "encode_ip_hdr") == 0) 631 proxy_type = PROXY_TYPE_ENCODE_IPHDR; 632 else if (strcmp(token, "encode_tcp_stream") == 0) 633 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; 634 else if (strcmp(token, "no_encode") == 0) 635 proxy_type = PROXY_TYPE_ENCODE_NONE; 636 else 637 return -1; 638 state = STATE_READ_KEYWORD; 639 break; 640 641 case STATE_READ_PORT: 642 strcpy(str_port, token); 643 state = STATE_READ_KEYWORD; 644 break; 645 646 case STATE_READ_SERVER: 647 { 648 int err; 649 char *p; 650 char s[sizeof(buffer)]; 651 652 p = token; 653 while (*p != ':' && *p != 0) 654 p++; 655 656 if (*p != ':') 657 { 658 err = IpAddr(token, &server_addr); 659 if (err) 660 return -1; 661 } 662 else 663 { 664 *p = ' '; 665 666 n = sscanf(token, "%s %s", s, str_server_port); 667 if (n != 2) 668 return -1; 669 670 err = IpAddr(s, &server_addr); 671 if (err) 672 return -1; 673 } 674 } 675 state = STATE_READ_KEYWORD; 676 break; 677 678 case STATE_READ_RULE: 679 n = sscanf(token, "%d", &rule_index); 680 if (n != 1 || rule_index < 0) 681 return -1; 682 state = STATE_READ_KEYWORD; 683 break; 684 685 case STATE_READ_DELETE: 686 { 687 int err; 688 int rule_to_delete; 689 690 if (token_count != 2) 691 return -1; 692 693 n = sscanf(token, "%d", &rule_to_delete); 694 if (n != 1) 695 return -1; 696 err = RuleNumberDelete(rule_to_delete); 697 if (err) 698 return -1; 699 return 0; 700 } 701 702 case STATE_READ_PROTO: 703 if (strcmp(token, "tcp") == 0) 704 proto = IPPROTO_TCP; 705 else if (strcmp(token, "udp") == 0) 706 proto = IPPROTO_UDP; 707 else 708 return -1; 709 state = STATE_READ_KEYWORD; 710 break; 711 712 case STATE_READ_SRC: 713 case STATE_READ_DST: 714 { 715 int err; 716 char *p; 717 struct in_addr mask; 718 struct in_addr addr; 719 720 p = token; 721 while (*p != '/' && *p != 0) 722 p++; 723 724 if (*p != '/') 725 { 726 IpMask(32, &mask); 727 err = IpAddr(token, &addr); 728 if (err) 729 return -1; 730 } 731 else 732 { 733 int nbits; 734 char s[sizeof(buffer)]; 735 736 *p = ' '; 737 n = sscanf(token, "%s %d", s, &nbits); 738 if (n != 2) 739 return -1; 740 741 err = IpAddr(s, &addr); 742 if (err) 743 return -1; 744 745 err = IpMask(nbits, &mask); 746 if (err) 747 return -1; 748 } 749 750 if (state == STATE_READ_SRC) 751 { 752 src_addr = addr; 753 src_mask = mask; 754 } 755 else 756 { 757 dst_addr = addr; 758 dst_mask = mask; 759 } 760 } 761 state = STATE_READ_KEYWORD; 762 break; 763 764 default: 765 return -1; 766 break; 767 } 768 769 do { 770 token = strsep(&res, " \t"); 771 } while (token != NULL && !*token); 772 } 773 #undef STATE_READ_KEYWORD 774 #undef STATE_READ_TYPE 775 #undef STATE_READ_PORT 776 #undef STATE_READ_SERVER 777 #undef STATE_READ_RULE 778 #undef STATE_READ_DELETE 779 #undef STATE_READ_PROTO 780 #undef STATE_READ_SRC 781 #undef STATE_READ_DST 782 783 /* Convert port strings to numbers. This needs to be done after 784 the string is parsed, because the prototype might not be designated 785 before the ports (which might be symbolic entries in /etc/services) */ 786 787 if (strlen(str_port) != 0) 788 { 789 int err; 790 791 err = IpPort(str_port, proto, &proxy_port); 792 if (err) 793 return -1; 794 } 795 else 796 { 797 proxy_port = 0; 798 } 799 800 if (strlen(str_server_port) != 0) 801 { 802 int err; 803 804 err = IpPort(str_server_port, proto, &server_port); 805 if (err) 806 return -1; 807 } 808 else 809 { 810 server_port = 0; 811 } 812 813 /* Check that at least the server address has been defined */ 814 if (server_addr.s_addr == 0) 815 return -1; 816 817 /* Add to linked list */ 818 proxy_entry = malloc(sizeof(struct proxy_entry)); 819 if (proxy_entry == NULL) 820 return -1; 821 822 proxy_entry->proxy_type = proxy_type; 823 proxy_entry->rule_index = rule_index; 824 proxy_entry->proto = proto; 825 proxy_entry->proxy_port = htons(proxy_port); 826 proxy_entry->server_port = htons(server_port); 827 proxy_entry->server_addr = server_addr; 828 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; 829 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; 830 proxy_entry->src_mask = src_mask; 831 proxy_entry->dst_mask = dst_mask; 832 833 RuleAdd(proxy_entry); 834 835 return 0; 836 } 837